Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的形式进行上传处理。 总共可上传2个扩展文件,每个最大文件可为2GB,同时obb文件格式可自选。
官网地址:http://developer.android.com/google/play/expansion-files.html
1、在sdk Manager中下载对应的支持库,play_licensing及play_apk_expansion如下:
2、生成需要的obb文件,并在上传apk包的同时选择上传扩展文件。
obb文件生成可参考jobb工具生成(官网推荐), http://developer.android.com/tools/help/jobb.html
也可以直接用参考stackoverflow上Sanket Kachhela的回答(个人推荐):http://stackoverflow.com/questions/14495483/read-content-from-apk-expansion-file-from-obb-file#answer-18953778,直接利用zip压缩工具选择存储模式。
3、如果上述采用zip压缩的话,这里就还需要unzip准备,利用zipHelper直接解压下载包并正常使用。
参考stackoverflow上Bhavesh Hirpara的回答:http://stackoverflow.com/questions/14495483/read-content-from-apk-expansion-file-from-obb-file#answer-15150977
1、在eclipse打开需要的库工程,如下:
2、如果downloader_library出现错误 AESObfuscator cannot be resolved 则说明Eclipse没有选择自动编辑,勾选下project->build automatically即可。
3、添加游戏项目,并添加对应的依赖项目如下图:
1、添加对应的权限。
<manifest ...> <!-- Required to access Google Play Licensing --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> <!-- Required to download files from Google Play --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Required to poll the state of the network connection and respond to changes --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Required to check whether Wi-Fi is enabled --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- Required to read and write the expansion files on shared storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
2、从~sdkplay_apk_expansiondownloader_sample项目复制srccomexampleexpansiondownloader下的sample文件到游戏目录下,并对应修改包名的引用等。
a、将onCreate下的判断obb文件存在的注释处理添加如下:
// otherwise, download not needed so we fall through to // starting the movie validateXAPKZipFiles();
b、修改 validateXAPKZipFiles 方法,去除验证,直接添加解压方法到对应的游戏下载目录。
// 解压到游戏下载目录 private void upzipApkExFile(){ try { ZipResourceFile expansionFile = APKExpansionSupport .getAPKExpansionZipFile(this, VERSION_CODE, 0); ZipEntryRO[] zip = expansionFile.getAllEntries(); Log.i("upzipApkExFile", "mZipFileName : " + zip[0].mZipFileName); String path = getApplicationContext().getFilesDir().getAbsolutePath(); File file = new File(path + ""); ZipHelper.unzip(zip[0].mZipFileName, file); if (file.exists()) { Log.e("", "unzipped : " + file.getAbsolutePath()); } } catch (Exception e) { e.printStackTrace(); } } /** * Go through each of the Expansion APK files and open each as a zip file. * Calculate the CRC for each file and return false if any fail to match. * * @return true if XAPKZipFile is successful */ void validateXAPKZipFiles() { upzipApkExFile(); Intent myIntent = new Intent(this, AppActivity.class); //跳转到游戏Activity startActivity(myIntent); this.finish(); }
c、修改SampleDownloaderService文件下的BASE64_PUBLIC_KEY。将其改成对应的Google Play上申请的应用的Key。
3、添加对应下载接受服务监听。
<application ...> <service android:name="com.test123.expansion.downloader.SampleDownloaderService" /> <receiver android:name="com.test123.expansion.downloader.SampleAlarmReceiver" /> ... </application>
4、主游戏AppActivity处理。在启动的Activity中进行判断,如果已经下载并解压了文件则进入游戏,否则跳转到下载的Activity。
protected void onCreate(final Bundle savedInstanceState){ super.onCreate(savedInstanceState); String path = getApplicationContext().getFilesDir().getAbsolutePath(); File boolfile = new File(path + "/downloaded"); if(!boolfile.exists()) { Intent myIntent = new Intent(this, SampleDownloaderActivity.class); startActivity(myIntent); this.finish(); return; } Log.i("upzipApkExFile", "already unziped!!");
}
为了让下载的扩展文件直接应用到游戏中,我们要稍微修改下cocos2dx引擎的读取文件处理。
首先,找到引擎目录下cocos2dcocosplatformCCFileUtils.cpp文件。
其次,对方法进行如下修改,使其先在getWritablePath目录下需找文件。
std::string FileUtils::fullPathForFilename(const std::string &filename) const { if (filename.empty()) { return ""; } if (isAbsolutePath(filename)) { return filename; } // Already Cached ? auto cacheIter = _fullPathCache.find(filename); if(cacheIter != _fullPathCache.end()) { return cacheIter->second; } // Get the new file name. const std::string newFilename( getNewFilename(filename) ); std::string fullpathWritablePath = getWritablePath() + newFilename; if (isFileExistInternal(fullpathWritablePath)) { if (fullpathWritablePath.length() > 0) { // Using the filename passed in as key. //_fullPathCache.insert(std::make_pair(filename, fullpathWritablePath)); _fullPathCache[filename] = fullpathWritablePath; return fullpathWritablePath; } } std::string fullpath; for (const auto& searchIt : _searchPathArray) { for (const auto& resolutionIt : _searchResolutionsOrderArray) { fullpath = this->getPathForFilename(newFilename, resolutionIt, searchIt); if (fullpath.length() > 0) { // Using the filename passed in as key. _fullPathCache.insert(std::make_pair(filename, fullpath)); return fullpath; } } } if(isPopupNotify()){ CCLOG("cocos2d: fullPathForFilename: No file found at %s. Possible missing file.", filename.c_str()); } // The file wasn't found, return empty string. return ""; }
而这里getWritablePath获取的目录正是我们之前解压obb文件的目录getApplicationContext().getFilesDir().getAbsolutePath()。
因此,到这里就可以在游戏中跟使用在assert中文件一样使用obb解压出来的文件了。