公司开发的项目用到热更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导致的。所以一个线程遇到失败要把其他线程取消掉。