本文方案未经上线验证,仅示例测试,暂未发现问题。如果不对之处,欢迎大佬们指正!
一. 能够解决什么痛点?
近期由于Google宣布自2025年8月31日起,所有的App必须更新至API35;看到不少伙伴在论坛反馈升级至API35后打包遇到的各种问题。经本人一番折腾,捣鼓出了一套自定义Android工程的方案。大幅简化官方工程繁琐配置。
- 提高Cocos打包效率。众所周知,Cocos的默认安卓工程,打包需要编译700多个cpp,双架构就是1400多个cpp,而且全程CPU拉满,非常耗时。更难受的是每次因为别的问题Clean一下项目都要重新编译。而这个方案是直接使用引擎编译后的动态库,所以不需要编译引擎。
- 方便将Cocos引擎快速嵌入到已有的Android应用中。
- 摆脱工程对引擎的资源依赖,让安卓工程和引擎版本不再有任何关联
- 大幅简化工程
二. 准备工作
- 1.AndroidStudio
- 因为Google Pay要求必须35,这里使用最新版本的AndroidStudio 2024.3.2(最低版本是2024.2.1)
Gradle使用8.11.1,最低要求8.6.0
- 因为Google Pay要求必须35,这里使用最新版本的AndroidStudio 2024.3.2(最低版本是2024.2.1)
- 2.获取Cocos引擎动态库
- 本文将使用最新版本引擎CocosCreator3.8.7进行演示
- 在获取引擎动态库之前,我们必须要先修改一下引擎JNI的部分,否则在引擎初始化时,会由于拿不到Android上下文对象而报错。(官方工程不存在这个问题)
- 找到引擎中的JniCocosEntry.cpp,注释掉第44行代码
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosActivity_onCreateNative(JNIEnv *env, jobject activity, jobject context) { cc::JniHelper::init(env, context); //cc::JniHelper::setActivity(activity); }
- 直接创建一个空的Cocos项目,构建默认Android工程,在AS中打开直接打release包,然后在Cocos项目文件夹搜索.so。就可以看到libcocos.so文件;这里需要选择relase版本的so
- 点进去后,我们通常需要双架构的就把这两个文件夹都备份,后续再不修改引擎的情况下,可以一直使用这份动态库。
三.创建Android工程
- 创建一个空的Android工程
- 这里选择No Acticity的模板,否则创建不出来Java版本的。
- 简单输入项目信息
- 注意语言选择Java。build配置文件语言选择Groovy。其他版本选择我们需要的即可
- 直接停止工程同步,改用国内源快速下载,顺便配置国内gradle依赖源
- 配置国内gradle
- 在gradle/wrapper/gradle-wrapper.properties中修改如下;
- distributionUrl是gradle版本的下载地址,部分小伙伴会卡在这里等待很久,我们这里需要改为国内源,快速下载。
#Fri Jul 04 11:25:18 CST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
- 配置gradle插件和依赖库下载地址
- 在setting.gradle中修改如下
pluginManagement { repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/jcenter' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/central' } google { content { includeGroupByRegex("com\\.android.*") includeGroupByRegex("com\\.google.*") includeGroupByRegex("androidx.*") } } mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/jcenter' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/central' } google() mavenCentral() } } rootProject.name = "testJavaAndroid" include ':app'
- 完成上述操作后,直接同步等待下载结束即可
- 配置国内gradle
四.导入Cocos所需文件
-
导入Cocos在Android的java代码,以及引擎依赖的几个库。
- 先把测试模块直接删除,不需要测试模块。
- 在app下创建目录libs,将cocos引擎的依赖库和我们编译好的引擎动态库放入进去。
- 依赖库直接去cocos引擎里找,很快就能找到
- Creator\3.8.7\resources\resources\3d\engine-native\cocos\platform\android\java\libs
路径示意 --app/ --libs/ --arm64-v8a/ --libcocos.so --libEGL.so --armeabi-v7a/ --libcocos.so --libEGL.so --com.android.vending.expansion.zipfile.jar --game-sdk.jar --okio-1.15.0.jar --okhttp-3.12.14.jar
- 完成后,修改app下的build.gradle配置文件, 在dependencies中增加 api fileTree(dir: ‘libs’, include: ‘*.jar’) ,后面会分享完整版配置
-
导入引擎在Android端的Java适配代码
- 完成上述操作后,我们需要把引擎的Java代码导入到工程中来,这里后续也可以打个jar直接导入。这里一定要注意目录结构不要搞错,也可以直接在文件夹里操作,避免出错;
- \Creator\3.8.7\resources\resources\3d\engine\templates\android\build\libservice\src\com
- Creator\3.8.7\resources\resources\3d\engine-native\cocos\platform\android\java\src\com
- 将上面路径中的文件夹拷贝到Android工程中app/src/main/java/下
- 正确的结构目录示意:
--src/ --main/ --java/ --com/ --cocos/ --lib/ --service/ --你的包名。。。
-
创建游戏主Activity
- 在src/main/java/(你的包名)下创建Java类AppGameActivity
- 代码中的内容,我们直接复制Cocos默认工程的主Activity就好。记得修改包名
package 你的包名; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import com.cocos.lib.CocosActivity; import com.cocos.service.SDKWrapper; public class AppGameActivity extends CocosActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // DO OTHER INITIALIZATION BELOW SDKWrapper.shared().init(this); } @Override protected void onResume() { super.onResume(); SDKWrapper.shared().onResume(); } @Override protected void onPause() { super.onPause(); SDKWrapper.shared().onPause(); } @Override protected void onDestroy() { super.onDestroy(); // Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508 if (!isTaskRoot()) { return; } SDKWrapper.shared().onDestroy(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); SDKWrapper.shared().onActivityResult(requestCode, resultCode, data); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); SDKWrapper.shared().onNewIntent(intent); } @Override protected void onRestart() { super.onRestart(); SDKWrapper.shared().onRestart(); } @Override protected void onStop() { super.onStop(); SDKWrapper.shared().onStop(); } @Override public void onBackPressed() { SDKWrapper.shared().onBackPressed(); super.onBackPressed(); } @Override public void onConfigurationChanged(Configuration newConfig) { SDKWrapper.shared().onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { SDKWrapper.shared().onRestoreInstanceState(savedInstanceState); super.onRestoreInstanceState(savedInstanceState); } @Override protected void onSaveInstanceState(Bundle outState) { SDKWrapper.shared().onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override protected void onStart() { SDKWrapper.shared().onStart(); super.onStart(); } @Override public void onLowMemory() { SDKWrapper.shared().onLowMemory(); super.onLowMemory(); } }
- 完成后,在AndroidManifest.xml中配置主Activity;并设置为启动Activity,样式选择全屏的(后面分析完整xml)
-
创建GameApplication类,并提供获取实例方法
- 在src/main/java/(你的包名)下创建Java类GameApplication
package 你的包名; import android.app.Application; public class GameApplication extends Application { private static GameApplication instance; @Override public void onCreate() { super.onCreate(); instance = this; // 保存全局实例 } public static GameApplication getApplication() { return instance; } }
- 在AndroidManifest.xml中配置GameApplication
- 在src/main/java/(你的包名)下创建Java类GameApplication
-
修改CocosActivity类
- 刚才我们修改了JniCocosEntry.cpp,现在需要修改下对应的Java层方法,在onCreateNative中,传入当前的GameApplication实例即可
//原 //private native void onCreateNative(); //新 private native void onCreateNative(Context context); @Override protected void onCreate(Bundle savedInstanceState) { onLoadNativeLibraries(); //原 //onCreateNative(); //新 onCreateNative(GameApplication.getApplication()); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().requestFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); ... }
-
在AndroidManifest.xml的application中增加meta配置
<meta-data android:name="android.app.lib_name" android:value="cocos"/>
- 因为在CocosActivity中onLoadNativeLibraries函数需要根据这个key读取动态库文件。这里value配置为cocos。可不要配置成libcocos哦。名字可以自定义改需要与实际的so库对应,不需要前面的"lib",本文尽可能不修改引擎原代码
-
完整版AndroidManifest.xml参考
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:name=".GameApplication" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.TestCocosAndroidProject" tools:targetApi="31" > <meta-data android:name="android.app.lib_name" android:value="cocos"/> <activity android:name=".AppGameActivity" android:exported="true" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
五.修改build.gradle配置文件
-
修改app下的build.gradle文件,在defauleConfig中增加ndk配置
ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' // 只保留需要的架构 }
-
指定动态库所在位置,配置assets目录
sourceSets { main { jniLibs.srcDirs = ['libs'] assets.srcDirs = ['assets'] } }
-
完整版build.gradle参考
- 仅示例工程,配置较简单
plugins { alias(libs.plugins.android.application) } android { namespace 'com.example.testjavaandroid' compileSdk 35 defaultConfig { applicationId "com.example.testjavaandroid" minSdk 21 targetSdk 35 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' // 只保留需要的架构 } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } buildFeatures { compose true } sourceSets { main { jniLibs.srcDirs = ['libs'] assets.srcDirs = ['assets'] } } } dependencies { api fileTree(dir: 'libs', include: '*.jar') implementation libs.appcompat implementation libs.material }
六.创建所需资源
-
左上角切到Android视图,创建文件夹assets,并拷贝构建好的Cocos资源放进去
-
在strings.xml中,增加Cocos所需的一些string。
- 这里主要是看到CocosEditBoxActivity中有一些string的使用。 增加后还需在CocosEditBoxActivity重新导入
<resources> <string name="app_name">你的游戏名字</string> <string name="done" translatable="false">Done</string> <string name="next" translatable="false">Next</string> <string name="search" translatable="false">Search</string> <string name="go" translatable="false">Go</string> <string name="send" translatable="false">Send</string> <string name="tip_disable_safe_input_type" translatable="false">Please go to the system settings to turn off the security keyboard!</string> </resources>
七.直接打包运行
- 完整上面修改后,直接打release包运行即可
- 打出的包大小是 25M左右,与官方空工程无异