定制属于你的安卓工程—手把手教程

本文方案未经上线验证,仅示例测试,暂未发现问题。如果不对之处,欢迎大佬们指正!

一. 能够解决什么痛点?

近期由于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
  • 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'
        
    • 完成上述操作后,直接同步等待下载结束即可

四.导入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
  • 修改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左右,与官方空工程无异
5赞

好复杂啊。官方好像要出吧?。。。

1赞

Mark!!!

这里面很多都是android工程基础知识,真正关键的修改就几处

搞个视频流量更吸引

怎么才能修改为像webview一样嵌入activity里面,3x改成现在gameactivity这种方式以后没法和其他组件合用了