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、從~sdk\play_apk_expansion\downloader_sample項目復制src\com\example\expansion\downloader下的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引擎的讀取文件處理。
首先,找到引擎目錄下cocos2d\cocos\platform\CCFileUtils.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解壓出來的文件了。
