cocos2dx 3.16 已经支持使用Google obb扩展包
首先项目支持Google play service
AndroidManifest.xml相关权限和工程配置
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /
<activity
android:name="com.cocos2d.plugin.downloader.UnityDownloaderActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme" >
</activity>
<service android:name="com.cocos2d.plugin.downloader.UnityDownloaderService" />
<receiver android:name="com.cocos2d.plugin.downloader.UnityAlarmReceiver" />
Cocos2dxActivity增加变量和方法如下:
public static String FATE_OBB_PATH = "";
public static String FATE_OBB_Name = "";
@Override
protected void onCreate(final Bundle savedInstanceState) {
FATE_OBB_Name = getObbFileName();
FATE_OBB_PATH = getVirtualObbFileFullpath();
super.onCreate(savedInstanceState);
....
}
private String getObbFileName() {
PackageInfo info = null;
try {
info = getPackageManager().getPackageInfo(getPackageName(), 0);
String fileName = "main." + info.versionCode + "." + getPackageName() + ".obb";
Log.e("obb===fileName===", fileName);
return fileName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return "";
}
private String getVirtualObbFileFullpath(){
String _path = "";
File file = getObbDir();
if(file == null || !file.exists()){
file.mkdirs();
}
_path = file.getPath() + "/" + getObbFileName();
Log.e("obb===_path===", _path);
return _path;
}
Cocos2dxHelper增加代码如下:
public static ZipResourceFile obbzip = null;
public static void init(final Activity activity) {
...
// begin--------------------添加代码----------------------------
// //检查obb文件是否存在
if (fileIsExists(Cocos2dxActivity.FATE_OBB_PATH)) {
// 存在添加obb路径到cocos中 注意 nativeSetObbPath 方法是需要新添加的 下方会介绍
Cocos2dxHelper.nativeSetObbPath(Cocos2dxActivity.FATE_OBB_PATH);
}
// end--------------------添加代码----------------------------
...
//设置压缩包
PackageInfo info = null;
try {
info = activity.getPackageManager().getPackageInfo(
activity.getPackageName(), 0);
Cocos2dxHelper.obbzip = APKExpansionSupport
.getAPKExpansionZipFile(activity, info.versionCode, 0);
} catch (PackageManager.NameNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
...
}
// 检查obb文件是否存在
public static boolean fileIsExists(String strFile) {
if(strFile.isEmpty()){
return false;
}
try {
File f = new File(strFile);
if (!f.exists()) {
return false;
}
} catch (Exception e) {
return false;
}
Log.d("obb", "file exit");
return true;
}
// nativeSetObbPath 设置obb路径方法
private static native void nativeSetObbPath(final String pObbPath);
Cocos2dxSound中:
soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(path), 0);
替换为
final AssetFileDescriptor assetFileDescritor = Cocos2dxHelper.obbzip
.getAssetFileDescriptor("assets/" + path);
if (assetFileDescritor == null) {
final AssetFileDescriptor assetFileDescritor1 = this.mContext
.getAssets().openFd(path);
soundID = this.mSoundPool.load(assetFileDescritor1, 0);
} else {
soundID = this.mSoundPool.load(assetFileDescritor, 0);
}
Cocos2dxMusic中:
mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(),assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
替换为
final AssetFileDescriptor assetFileDescritor = Cocos2dxHelper.obbzip.getAssetFileDescriptor("assets/"+path);
if(assetFileDescritor == null){
final AssetFileDescriptor assetFileDescritor1 = this.mContext.getAssets().openFd(path);
mediaPlayer.setDataSource(assetFileDescritor1.getFileDescriptor(), assetFileDescritor1.getStartOffset(), assetFileDescritor1.getLength());
}else{
mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
}
接下来就是改造c++中调用assets里资源的方式,只需要修改Java_org_cocos2dx_lib_Cocos2dxHelper和CCFileUtils-android两个文件
Java_org_cocos2dx_lib_Cocos2dxHelper.cpp中
//添加obb path
string g_obbPath;
//添加设置obbpath 方法
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxHelper_nativeSetObbPath(JNIEnv* env, jobject thiz, jstring obbPath) {
g_obbPath = JniHelper::jstring2string(obbPath);
}
//添加获取obbpath 方法
const char * getObbPath() {
return g_obbPath.c_str();
}
Java_org_cocos2dx_lib_Cocos2dxHelper.h中
//添加方法
extern const char * getObbPath();
CCFileUtils-android.cpp中
isFileExistInternal方法
bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const
{
if (strFilePath.empty())
{
return false;
}
// if (cocosplay::isEnabled() && !cocosplay::isDemo())
// {
// return cocosplay::fileExists(strFilePath);
// }
bool bFound = false;
// Check whether file exists in apk.
if (strFilePath[0] != '/')
{
const char* s = strFilePath.c_str();
// Found "assets/" at the beginning of the path and we don't want it
if (strFilePath.find(_defaultResRootPath) == 0) s += strlen("assets/");
if (FileUtilsAndroid::assetmanager) {
AAsset* aa = AAssetManager_open(FileUtilsAndroid::assetmanager, s, AASSET_MODE_UNKNOWN);
if (aa)
{
bFound = true;
AAsset_close(aa);
} else if(getObbPath() != nullptr){
// CCLOG("[AssetManager] ... in APK %s,%s, found = false! \n obbPath=%s", strFilePath.c_str(),s, getObbPath());
ZipFile obbFile(getObbPath());
if ( obbFile.fileExists(strFilePath.c_str()) ) {
// CCLOG("obb ... in APK %s, found = true!", strFilePath.c_str());
bFound = true;
}else{
// CCLOG("obb ... in APK %s, found = false!", strFilePath.c_str());
}
}
}
}
else
{
FILE *fp = fopen(strFilePath.c_str(), "r");
if(fp)
{
bFound = true;
fclose(fp);
}
}
return bFound;
}
getData方法
Data FileUtilsAndroid::getData(const std::string& filename, bool forString)
{
if (filename.empty())
{
return Data::Null;
}
unsigned char* data = nullptr;
ssize_t size = 0;
string fullPath = fullPathForFilename(filename);
// cocosplay::updateAssets(fullPath);
if (fullPath[0] != '/')
{
string relativePath = string();
size_t position = fullPath.find("assets/");
if (0 == position) {
// "assets/" is at the beginning of the path and we don't want it
relativePath += fullPath.substr(strlen("assets/"));
} else {
relativePath += fullPath;
}
CCLOGINFO("relative path = %s", relativePath.c_str());
if (nullptr == FileUtilsAndroid::assetmanager) {
LOGD("... FileUtilsAndroid::assetmanager is nullptr");
return Data::Null;
}
// read asset data
AAsset* asset =
AAssetManager_open(FileUtilsAndroid::assetmanager,
relativePath.c_str(),
AASSET_MODE_UNKNOWN);
if (nullptr != asset) {
off_t fileSize = AAsset_getLength(asset);
if (forString)
{
data = (unsigned char*) malloc(fileSize + 1);
data[fileSize] = '\0';
}
else
{
data = (unsigned char*) malloc(fileSize);
}
int bytesread = AAsset_read(asset, (void*)data, fileSize);
size = bytesread;
// CCLOG("obb size test: size=%d;fileSize=%d", size, fileSize);
AAsset_close(asset);
// return Data::Null;
}else{
// CCLOG("asset is nullptr");
ZipFile zipFile(getApkPath());
// CCLOG("guok special read: %s;forString=%d", relativePath.c_str(), forString);
string zFileNamePath = "assets/" + relativePath;
if ( zipFile.fileExists(zFileNamePath.c_str()) ) {
ssize_t fsize = 0;
unsigned char* content = zipFile.getFileData(zFileNamePath.c_str(), &fsize);
if( fsize > 0) {
// CCLOG("guok read success %d", fsize);
memcpy( data, content, fsize);
size = fsize;
}
else {
// CCLOG("guok read error %d", fsize);
}
if(content)free(content);
}
else if(getObbPath() != nullptr){
ZipFile obbFile(getObbPath());
if ( obbFile.fileExists(zFileNamePath.c_str()) ) {
ssize_t fsize = 0;
unsigned char* content = obbFile.getFileData(zFileNamePath.c_str(), &fsize);
if( fsize > 0) {
if (forString)
{
data = (unsigned char*) malloc(fsize + 1);
data[fsize] = '\0';
}else{
data = (unsigned char*) malloc(fsize);
}
memcpy( data, content, fsize);
size = fsize;
// CCLOG("obb getdata success %d", fsize);
} else {
CCLOG("obb getdata error %d", fsize);
}
if(content)free(content);
}
}
if (size <= 0)
{
CCLOG("obb null size %d", size);
return Data::Null;
}
}
}
else
{
do
{
// read rrom other path than user set it
//CCLOG("GETTING FILE ABSOLUTE DATA: %s", filename);
const char* mode = nullptr;
if (forString)
mode = "rt";
else
mode = "rb";
FILE *fp = fopen(fullPath.c_str(), mode);
CC_BREAK_IF(!fp);
long fileSize;
fseek(fp,0,SEEK_END);
fileSize = ftell(fp);
fseek(fp,0,SEEK_SET);
if (forString)
{
data = (unsigned char*) malloc(fileSize + 1);
data[fileSize] = '\0';
}
else
{
data = (unsigned char*) malloc(fileSize);
}
fileSize = fread(data,sizeof(unsigned char), fileSize,fp);
fclose(fp);
size = fileSize;
} while (0);
}
Data ret;
if (data == nullptr || size == 0)
{
std::string msg = "Get data from file(";
msg.append(filename).append(") failed!");
CCLOG("%s", msg.c_str());
}
else
{
ret.fastSet(data, size);
// cocosplay::notifyFileLoaded(fullPath);
}
return ret;
}
getFileData方法
unsigned char* FileUtilsAndroid::getFileData(const std::string& filename, const char* mode, ssize_t * size)
{
unsigned char * data = 0;
// CCLOG("obb getFileData:%s",filename.c_str());
if ( filename.empty() || (! mode) )
{
return 0;
}
string fullPath = fullPathForFilename(filename);
// cocosplay::updateAssets(fullPath);
if (fullPath[0] != '/')
{
string relativePath = string();
size_t position = fullPath.find("assets/");
if (0 == position) {
// "assets/" is at the beginning of the path and we don't want it
relativePath += fullPath.substr(strlen("assets/"));
} else {
relativePath += fullPath;
}
LOGD("relative path = %s", relativePath.c_str());
if (nullptr == FileUtilsAndroid::assetmanager) {
LOGD("... FileUtilsAndroid::assetmanager is nullptr");
return nullptr;
}
// read asset data
AAsset* asset =
AAssetManager_open(FileUtilsAndroid::assetmanager,
relativePath.c_str(),
AASSET_MODE_UNKNOWN);
if (nullptr != asset) {
// LOGD("asset is nullptr");
off_t fileSize = AAsset_getLength(asset);
data = (unsigned char*) malloc(fileSize);
int bytesread = AAsset_read(asset, (void*)data, fileSize);
if (size)
{
*size = bytesread;
}
AAsset_close(asset);
} else {
ZipFile zipFile(getApkPath());
// CCLOG(" special read: %s", relativePath.c_str());
string zFileNamePath = "assets/" + relativePath;
if ( zipFile.fileExists(zFileNamePath.c_str()) ) {
ssize_t fsize = 0;
unsigned char* content = zipFile.getFileData(zFileNamePath.c_str(), &fsize);
if( fsize > 0) {
// CCLOG("getfiledata success %d", fsize);
data = (unsigned char*) malloc(fsize);
memcpy( data, content, fsize);
if (size)
{
*size = fsize;
}
}
else {
CCLOG("getfiledata error %d", fsize);
}
if(content)free(content);
} else if(getObbPath() != nullptr){
ZipFile obbFile(getObbPath());
if ( obbFile.fileExists(zFileNamePath.c_str()) ) {
ssize_t fsize = 0;
unsigned char* content = obbFile.getFileData(zFileNamePath.c_str(), &fsize);
if( fsize > 0) {
// CCLOG("obb getfiledata success %d", fsize);
data = (unsigned char*) malloc(fsize);
memcpy( data, content, fsize);
if (size)
{
*size = fsize;
}
} else {
CCLOG("obb getfiledata error %d", fsize);
}
if(content)free(content);
}
}
}
if (!data)
{
// CCLOG("getFileData error: %s", relativePath.c_str());
return nullptr;
}
}
else
{
do
{
// read rrom other path than user set it
//CCLOG("GETTING FILE ABSOLUTE DATA: %s", filename);
FILE *fp = fopen(fullPath.c_str(), mode);
CC_BREAK_IF(!fp);
long fileSize;
fseek(fp,0,SEEK_END);
fileSize = ftell(fp);
fseek(fp,0,SEEK_SET);
data = (unsigned char*) malloc(fileSize);
fileSize = fread(data,sizeof(unsigned char), fileSize,fp);
fclose(fp);
if (size)
{
*size = fileSize;
}
} while (0);
}
if (! data)
{
std::string msg = "Get data from file(";
msg.append(filename).append(") failed!");
CCLOG("%s", msg.c_str());
}
else
{
// cocosplay::notifyFileLoaded(fullPath);
}
return data;
}
主activity的oncreate方法添加一下代码检测是否已经下载到了obb包
if (Cocos2dxActivity.FATE_OBB_PATH.isEmpty() || !Cocos2dxHelper.fileIsExists(Cocos2dxActivity.FATE_OBB_PATH)) {
Intent intent = new Intent(GameContext.getGameInstance(), UnityDownloaderActivity.class);
GameContext.getGameInstance().startActivity(intent);
有部分代码和工程需要下载,请https://download.csdn.net/download/bibi333/10324625
https://download.csdn.net/download/bibi333/10324619
这些准备之后,就是上传Google beta包测试了