公司开发的项目用到热更AssetsManager。其中遇到几个问题,这里与大家分享一下。
1.多个文件需要更新,没有全部更新成功。然而更新成功的文件使用了。导致异常。
解决方案:
(1)首先需要使用热更临时目录,全部热更成功再把文件移动到热更目录。
_storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath()+"/update": "./update");
_storageTempPath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath()+"/temp" : "./temp");
(2)手动添加searchpaths,注释掉AssetsManager的添加searchpath代码。
void AssetsManager::setSearchPath()
{
//vector<string> searchPaths = FileUtils::getInstance()->getSearchPaths();
//vector<string>::iterator iter = searchPaths.begin();
//searchPaths.insert(iter, _storagePath);
//FileUtils::getInstance()->setSearchPaths(searchPaths);
}
var searchPaths = jsb.fileUtils.getSearchPaths();
searchPaths.unshift(_storagePath);
(3)在android/ios增加创建文件,复制文件,删除文件的接口。
主要注意ios的文件创建,以及热更前需要清空临时文件夹。不然会导致异常。
android:
//删除文件夹和文件夹里面的文件
public static void deleteDir(final String pPath) {
File dir = new File(pPath);
deleteDirWihtFile(dir);
}
public static void deleteDirWihtFile(File dir) {
if (dir == null || !dir.exists() || !dir.isDirectory())
return;
for (File file : dir.listFiles()) {
if (file.isFile())
file.delete(); // 删除所有文件
else if (file.isDirectory())
deleteDirWihtFile(file); // 递规的方式删除文件夹
}
dir.delete();// 删除目录本身
}
public static void createDir(String path) {
(new File(path)).mkdirs(); //如果文件夹不存在 则建立新文件夹
}
/**
* 复制单个文件
* @param oldPath String 原文件路径 如:c:/fqf.txt
* @param newPath String 复制后路径 如:f:/fqf.txt
* @return boolean
*/
public static void copyFile(String oldPath, String newPath) {
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
if (oldfile.exists()) { //文件存在时
InputStream inStream = new FileInputStream(oldPath); //读入原文件
FileOutputStream fs = new FileOutputStream(newPath);
byte[] buffer = new byte[1024];
int length;
while ( (byteread = inStream.read(buffer)) != -1) {
bytesum += byteread; //字节数 文件大小
System.out.println(bytesum);
fs.write(buffer, 0, byteread);
}
inStream.close();
}
}
catch (Exception e) {
System.out.println("复制单个文件操作出错");
e.printStackTrace();
}
}
ios:
//复制
+ (void)copyFile:(NSString*) oldPath newPath:(NSString*) newPath
{
NSFileManager *fileManger = [NSFileManager defaultManager];
BOOL isDir;
if ([fileManger fileExistsAtPath:oldPath isDirectory:&isDir] && isDir) {
NSArray *pathArray = [fileManger contentsOfDirectoryAtPath:oldPath error:nil];
for (NSString *path in pathArray) {
NSLog(@"copy old path is %@",path);
NSString *dOldpath = [NSString stringWithFormat:@"%@/%@",oldPath,path];
NSString *dNewpath = [NSString stringWithFormat:@"%@/%@",newPath,path];
[AppController copyFile:dOldpath newPath:dNewpath];
}
}else{
BOOL isCopy = [fileManger copyItemAtPath:oldPath toPath:newPath error:nil];
NSLog(@"path is copy %@%d",oldPath,isCopy);
}
}
//删除
+ (void)deleteDir:(NSString*)path
{
NSFileManager *fileManger = [NSFileManager defaultManager];
BOOL isDelete = [fileManger removeItemAtPath:path error:nil];
NSLog(@"%d",isDelete);
}
//创建
+(void)createDir:(NSString*)path{
NSFileManager *fileManger = [NSFileManager defaultManager];
if (![fileManger fileExistsAtPath:path]) {
NSLog(@"file not find %@",path);
[fileManger createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
}
2.ios设备下载文件错误导致的崩溃问题。
解决方案:修改CCDownloader-apple.mm下的
- (void)URLSession:(NSURLSession *)session task :(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
DLLOG("DownloaderAppleImpl task: \"%s\" didCompleteWithError: %d errDesc: %s"
, [task.originalRequest.URL.absoluteString cStringUsingEncoding:NSUTF8StringEncoding]
, (error ? (int)error.code: 0)
, [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]);
// clean wrapper C++ object
DownloadTaskWrapper *wrapper = [self.taskDict objectForKey:task];
if(_outer)
{
if(error)
{
std::vector<unsigned char> buf; // just a placeholder
_outer->onTaskFinish(*[wrapper get],
cocos2d::network::DownloadTask::ERROR_IMPL_INTERNAL,
(int)error.code,
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding],
buf);
}
else if (![wrapper get]->storagePath.length())
{
// call onTaskFinish for a data task
// (for a file download task, callback is called in didFinishDownloadingToURL)
std::string errorString;
const int64_t buflen = [wrapper totalBytesReceived];
char buf[buflen];
[wrapper transferDataToBuffer:buf lengthOfBuffer:buflen];
std::vector<unsigned char> data(buf, buf + buflen);
_outer->onTaskFinish(*[wrapper get],
cocos2d::network::DownloadTask::ERROR_NO_ERROR,
0,
errorString,
data);
}
else
{
NSInteger statusCode = ((NSHTTPURLResponse*)task.response).statusCode;
// Check for error status code
if (statusCode >= 400)
{
std::vector<unsigned char> buf; // just a placeholder
const char *orignalURL = [task.originalRequest.URL.absoluteString cStringUsingEncoding:NSUTF8StringEncoding];
std::string errorMessage = cocos2d::StringUtils::format("Downloader: Failed to download %s with status code (%d)", orignalURL, (int)statusCode);
_outer->onTaskFinish(*[wrapper get],
cocos2d::network::DownloadTask::ERROR_IMPL_INTERNAL,
0,
errorMessage,
buf);
while (!_taskQueue.empty() && _taskQueue.front()) {
if (_taskQueue.front() != nil) {
[_taskQueue.front() cancel];
}
_taskQueue.pop();
}
}
}
}
[self.taskDict removeObjectForKey:task];
[wrapper release];
while (!_taskQueue.empty() && _taskQueue.front() == nil) {
_taskQueue.pop();
}
if (!_taskQueue.empty()) {
[_taskQueue.front() resume];
_taskQueue.pop();
}
}
这个问题是
AssetsManager对象已经被失败的线程释放了,后面的线程再使用了AssetsManager导致的。所以一个线程遇到失败要把其他线程取消掉。