Unity Android APP il2cpp 学习
相关设置
Assets\AndroidIl2cppPatchDemo\AndroidBuilder.cs
ANDROID_BUILD_TOOLS_VERSION, ANDROID_PLATFORM 可以自己选择对应的版本
sdkmanager.bat platforms;android-23
sdkmanager.bat build-tools;26.0.2
需要在生成的AndroidManifest.xml 中加入
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29"></uses-sdk>
不然会提示 "此应用专为旧版android打造,因此可能无法正常运行"
AndroidBuilder.cs
检查配置
public static bool ValidateConfig() { // e.g. C:/Users/NH55/AppData/Local/Android/Sdk string sdkPath = EditorPrefs.GetString("AndroidSdkRoot", ""); if (string.IsNullOrEmpty(sdkPath)) { Debug.LogError("sdk path is empty! please config via menu path:Edit/Preference->External tools."); return false; } // e.g. D:\\Unity\\2018.4.6f1\\Editor\\Data\\PlaybackEngines\\AndroidPlayer/Tools\\OpenJDK\\Windows string jdkPath = EditorPrefs.GetString("JdkPath", ""); if (string.IsNullOrEmpty(jdkPath)) { Debug.LogError("jdk path is empty! please config via menu path:Edit/Preference->External tools."); return false; } // D:/android-ndk-r16b string ndkPath = EditorPrefs.GetString("AndroidNdkRootR16b", ""); if (string.IsNullOrEmpty(ndkPath)) { ndkPath = EditorPrefs.GetString("AndroidNdkRoot", ""); if (string.IsNullOrEmpty(ndkPath)) { Debug.LogError("ndk path is empty! please config via menu path:Edit/Preference->External tools."); return false; } } // C:/Users/NH55/AppData/Local/Android/Sdk/build-tools/29.0.2/ string buildToolPath = sdkPath + "/build-tools/" + ANDROID_BUILD_TOOLS_VERSION + "/"; if (!Directory.Exists(buildToolPath)) { Debug.LogError("Android Build Tools not found. Try to reconfig version on the top of AndroidBuilder.cs. In Unity2018, it can't be work if less than 26.0.2. current:" + buildToolPath); return false; } // C:/Users/NH55/AppData/Local/Android/Sdk/platforms/android-29/android.jar string platformJar = sdkPath + "/platforms/" + ANDROID_PLATFORM + "/android.jar"; if (!File.Exists(platformJar)) { Debug.LogError("Android Platform not found. Try to reconfig version on the top of AndroidBuilder.cs. current:" + platformJar); return false; } Debug.Log("Build Env is ready!"); Debug.Log("Build Options:"); Debug.Log("SDK PATH=" + sdkPath); Debug.Log("JDK PATH=" + jdkPath); Debug.Log("BUILD TOOLS PATH=" + buildToolPath); Debug.Log("ANDROID PLATFORM=" + platformJar); return true; }
1. 导出Gradle项目
// 导出 Gradle Project [MenuItem("AndroidBuilder/Step 1: Export Gradle Project", false, 101)] public static bool ExportGradleProject() { //build settings if (!ValidateConfig()) { return false; } PlayerSettings.applicationIdentifier = "cn.noodle1983.unitypatchdemo"; PlayerSettings.companyName = "noodle1983"; PlayerSettings.productName = "UnityAndroidIl2cppPatchDemo"; EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle; PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP); PlayerSettings.stripEngineCode = false; #if UNITY_2018 || UNITY_2019 //PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7; PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.X86 | AndroidArchitecture.ARM64; #endif //export project string error_msg = string.Empty; string[] levels = new string[] { "Assets/AndroidIl2cppPatchDemo/Scene/0.unity" }; BuildOptions options = BuildOptions.AcceptExternalModificationsToPlayer; if (Directory.Exists(ANDROID_EXPORT_PATH)) { FileUtil.DeleteFileOrDirectory(ANDROID_EXPORT_PATH);} Directory.CreateDirectory(ANDROID_EXPORT_PATH); try { #if UNITY_2018 || UNITY_2019 error_msg = BuildPipeline.BuildPlayer(levels, ANDROID_EXPORT_PATH, EditorUserBuildSettings.activeBuildTarget, options).summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded ? string.Empty : "Failed to export project!"; #else error_msg = BuildPipeline.BuildPlayer(levels, ANDROID_EXPORT_PATH, EditorUserBuildSettings.activeBuildTarget, options); #endif } catch (Exception e) { Debug.LogError(e.ToString()); return false; } if (!string.IsNullOrEmpty(error_msg)) { Debug.LogError(error_msg); return false; } //copy the prebuild patch to the assets directory instead of downloading. // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/assets/AllAndroidPatchFiles_Version1.zip FileUtil.CopyFileOrDirectory(PROJECT_DIR + "/Assets/AndroidIl2cppPatchDemo/PrebuiltPatches/AllAndroidPatchFiles_Version1.zip", EXPORTED_ASSETS_PATH + "/AllAndroidPatchFiles_Version1.zip"); FileUtil.CopyFileOrDirectory(PROJECT_DIR + "/Assets/AndroidIl2cppPatchDemo/PrebuiltPatches/AllAndroidPatchFiles_Version2.zip", EXPORTED_ASSETS_PATH + "/AllAndroidPatchFiles_Version2.zip"); return true; }
2. 修改 UnityPlayerActivity.java
// 修改 Gradle Project [MenuItem("AndroidBuilder/Step 2: Patch Gradle Project", false, 102)] public static bool PatchAndroidProject() { //1. patch java file string[] javaEntranceFiles = Directory.GetFiles(JAVA_SRC_PATH, "UnityPlayerActivity.java", SearchOption.AllDirectories); if (javaEntranceFiles.Length != 1) { Debug.LogError("UnityPlayerActivity.java not found or more than one."); return false; } string javaEntranceFile = javaEntranceFiles[0]; string allJavaText = File.ReadAllText(javaEntranceFile); if (allJavaText.IndexOf("bootstrap") > 0) { Debug.Log("UnityPlayerActivity.java already patched."); return true; } allJavaText = allJavaText.Replace("import android.view.WindowManager;", @"import android.view.WindowManager; import io.github.noodle1983.Boostrap;"); allJavaText = allJavaText.Replace("mUnityPlayer = new UnityPlayer(this);", @"Boostrap.InitNativeLibBeforeUnityPlay(getApplication().getApplicationContext().getFilesDir().getPath()); mUnityPlayer = new UnityPlayer(this);"); File.WriteAllText(javaEntranceFile, allJavaText); return true; }
4. 生成 build_apk.bat
// 生成 build_apk.bat [MenuItem("AndroidBuilder/Step 4: Generate Build Scripts", false, 104)] public static bool GenerateBuildScripts() { string sdkPath = EditorPrefs.GetString("AndroidSdkRoot", ""); string jdkPath = EditorPrefs.GetString("JdkPath", ""); ; if (string.IsNullOrEmpty(sdkPath) || string.IsNullOrEmpty(jdkPath)) { Debug.LogError("sdk/jdk path is empty! please config via menu path:Edit/Preference->External tools."); return false; } StringBuilder allCmd = new StringBuilder(); // gen R.java string buildToolPath = sdkPath + "/build-tools/" + ANDROID_BUILD_TOOLS_VERSION + "/"; // e.g. C:/Users/NH55/AppData/Local/Android/Sdk/build-tools/29.0.2//aapt.exe // aapt = Android Asset Packaging Tool string aaptPath = buildToolPath + "/aapt.exe"; // e.g. C:/Users/NH55/AppData/Local/Android/Sdk/platforms/android-29/android.jar string platformJar = sdkPath + "/platforms/" + ANDROID_PLATFORM + "/android.jar"; // e.g. // package -f -m // -J D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/gen/ // - M D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/AndroidManifest.xml // - S D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/res // - I C:/Users/NH55/AppData/Local/Android/Sdk/platforms/android-29/android.jar string genRJavaCmd = " package -f -m " + " -J " + R_JAVA_PATH + " -M " + MANIFEST_XML_PATH + " -S " + RES_PATH + " -I " + platformJar; if (!Directory.Exists(R_JAVA_PATH)) { Directory.CreateDirectory(R_JAVA_PATH); } allCmd.AppendFormat("call \"{0}\" {1}\n\n", aaptPath, genRJavaCmd); //build java // e.g. D:\\Unity\\2018.4.6f1\\Editor\\Data\\PlaybackEngines\\AndroidPlayer/Tools\\OpenJDK\\Windows/bin/javac.exe string javacPath = jdkPath + "/bin/javac.exe"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/libs/unity-classes.jar string[] jarLibFiles = Directory.GetFiles(JAR_LIB_PATH, "*.jar", SearchOption.AllDirectories); // e.g. // D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo\\src\\main\\java\\cn\\noodle1983\\unitypatchdemo\\UnityPlayerActivity.java // D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo\\src\\main\\java\\io\\github\\noodle1983\\Boostrap.java string[] javaSrcFiles = Directory.GetFiles(ANDROID_PROJECT_PATH, "*.java", SearchOption.AllDirectories); // e.g. // -d D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/objs/ // -source 1.7 - target 1.7 // - classpath D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/libs/unity-classes.jar // - sourcepath D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/java/ // - bootclasspath C:/Users/NH55/AppData/Local/Android/Sdk/platforms/android-29/android.jar D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo\\src\\main\\java\\cn\\noodle1983\\unitypatchdemo\\UnityPlayerActivity.java D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo\\src\\main\\java\\io\\github\\noodle1983\\Boostrap.java string compileParam = "-d " + JAVA_OBJ_PATH + " -source 1.7 -target 1.7 " + " -classpath " + string.Join(";", jarLibFiles) + " -sourcepath " + JAVA_SRC_PATH + " -bootclasspath " + platformJar + " " + string.Join(" ", javaSrcFiles); if (!Directory.Exists(JAVA_OBJ_PATH)) { Directory.CreateDirectory(JAVA_OBJ_PATH); } allCmd.AppendFormat("call \"{0}\" {1}\n\n", javacPath, compileParam); //dx // e.g. C:/Users/NH55/AppData/Local/Android/Sdk/build-tools/29.0.2//dx.bat string dxPath = buildToolPath + "/dx.bat"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//pkg_raw string pkgRawPath = BUILD_SCRIPTS_PATH + "/pkg_raw"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//pkg_raw/classes.dex string dexPath = pkgRawPath + "/classes.dex"; // e.g. --dex --output=D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//pkg_raw/classes.dex D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/objs/ D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/libs/unity-classes.jar string deParam = " --dex --output=" + dexPath + " " + JAVA_OBJ_PATH + " " + string.Join(" ", jarLibFiles); if (!Directory.Exists(pkgRawPath)) { Directory.CreateDirectory(pkgRawPath); } allCmd.AppendFormat("call \"{0}\" {1}\n\n@echo on\n\n", dxPath, deParam); //prepare lib // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//pkg_raw/lib string outputLibPath = pkgRawPath + "/lib"; if (Directory.Exists(outputLibPath)) { FileUtil.DeleteFileOrDirectory(outputLibPath); } Directory.CreateDirectory(outputLibPath); FileUtil.ReplaceDirectory(SO_LIB_PATH + "/armeabi-v7a", outputLibPath + "/armeabi-v7a"); FileUtil.ReplaceDirectory(SO_LIB_PATH + "/x86", outputLibPath + "/x86"); #if UNITY_2018 || UNITY_2019 FileUtil.ReplaceDirectory(SO_LIB_PATH + "/arm64-v8a", outputLibPath + "/arm64-v8a"); FileUtil.DeleteFileOrDirectory(outputLibPath + "/arm64-v8a/Data"); #endif FileUtil.DeleteFileOrDirectory(outputLibPath + "/armeabi-v7a/Data"); FileUtil.DeleteFileOrDirectory(outputLibPath + "/x86/Data"); var debug_files = Directory.GetFiles(outputLibPath, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".debug") || s.EndsWith(".map") || s.EndsWith(".sym")); foreach (string file in debug_files) { File.Delete(file); } //build apk // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin string binPath = BUILD_SCRIPTS_PATH + "/bin"; if (!Directory.Exists(binPath)) { Directory.CreateDirectory(binPath); } // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo..unaligned.apk string unaligned_apk_path = binPath + "/" + Application.identifier + "." + ".unaligned.apk"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/assets string assetsPath = EXPORTED_ASSETS_PATH; // e.g. // package -f -m // -F D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo..unaligned.apk // -M D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/AndroidManifest.xml // -A D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/assets // -S D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/res // -I C:/Users/NH55/AppData/Local/Android/Sdk/platforms/android-29/android.jar D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//pkg_raw string buildApkParam = " package -f -m -F " + unaligned_apk_path + " -M " + MANIFEST_XML_PATH + " -A " + assetsPath + " -S " + RES_PATH + " -I " + platformJar + " " + pkgRawPath; allCmd.AppendFormat("call \"{0}\" {1}\n\n", aaptPath, buildApkParam); //align // e.g. C:/Users/NH55/AppData/Local/Android/Sdk/build-tools/29.0.2//zipalign.exe string zipalign = buildToolPath + "/zipalign.exe"; // e.g. cn.noodle1983.unitypatchdemo.apk string alignedApkName = Application.identifier + ".apk"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo.apk string alignedApkPath = binPath + "/" + alignedApkName; // e.g. -f 4 D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo..unaligned.apk D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo.apk string zipalignParam = " -f 4 " + unaligned_apk_path + " " + alignedApkPath; allCmd.AppendFormat("call \"{0}\" {1}\n\n", zipalign, zipalignParam); //sign // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidKeystore string keystoreDir = PROJECT_DIR + "/AndroidKeystore"; if (!Directory.Exists(keystoreDir)) { Directory.CreateDirectory(keystoreDir); } // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidKeystore/test.keystore string keystoreFile = keystoreDir + "/test.keystore"; if (!File.Exists(keystoreFile)) { // e.g. D:\\Unity\\2018.4.6f1\\Editor\\Data\\PlaybackEngines\\AndroidPlayer/Tools\\OpenJDK\\Windows/bin/keytool.exe string keytoolPath = jdkPath + "/bin/keytool.exe"; // e.g. -genkey -alias test -validity 1000 -keyalg RSA -keystore D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidKeystore/test.keystore -dname \"CN = Test, OU = Test, O = Test, L = Test, S = Test, C = Test\" -keysize 4096 -storepass testtest -keypass testtest string genKeyParam = "-genkey -alias test -validity 1000 -keyalg RSA -keystore " + keystoreFile + " -dname \"CN = Test, OU = Test, O = Test, L = Test, S = Test, C = Test\" -keysize 4096 -storepass testtest -keypass testtest"; if (!Exec(keytoolPath, genKeyParam)) { Debug.LogError("exec failed:" + keytoolPath + " " + genKeyParam); return false; } } // e.g. C:/Users/NH55/AppData/Local/Android/Sdk/build-tools/29.0.2//apksigner.bat string apksignerPath = buildToolPath + "/apksigner.bat"; // e.g. sign --ks D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidKeystore/test.keystore --ks-pass pass:testtest --key-pass pass:testtest D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo.apk string signParam = " sign --ks " + keystoreFile + " --ks-pass pass:testtest --key-pass pass:testtest " + alignedApkPath; allCmd.AppendFormat("call \"{0}\" {1}\n\n", apksignerPath, signParam); //del tmp apk allCmd.AppendFormat("del /f /a /Q {0}\n\n", unaligned_apk_path.Replace("/", "\\")); allCmd.AppendFormat("explorer.exe {0} \n\n", binPath.Replace("//", "/").Replace("/", "\\")); allCmd.AppendFormat("@echo on\n\n"); //explorer as the last line wont return success, so... // D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main/build_apk.bat File.WriteAllText(BUILD_SCRIPTS_PATH + "/build_apk.bat", allCmd.ToString()); //bugs for android-23 //https://issuetracker.unity3d.com/issues/android-build-fails-with-failed-to-repackage-resources-error-when-api-level-23-or-lower-is-used string manifestXmlPath = MANIFEST_XML_PATH; string content = File.ReadAllText(manifestXmlPath); File.WriteAllText(manifestXmlPath, content.Replace(@"|density", "")); return true; }
5. 生成Apk
// 生成Apk [MenuItem("AndroidBuilder/Step 5: Build Apk File", false, 105)] public static bool BuildApk() { // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//build_apk.bat string buildApkPath = BUILD_SCRIPTS_PATH + "/build_apk.bat"; // e.g. cn.noodle1983.unitypatchdemo.apk string alignedApkName = Application.identifier + ".apk"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.0/UnityAndroidIl2cppPatchDemo/src/main//bin/cn.noodle1983.unitypatchdemo.apk string alignedApkPath = BUILD_SCRIPTS_PATH + "/bin/" + alignedApkName; if (!Exec(buildApkPath, "")) { Debug.LogError("exec failed:" + buildApkPath); return false; } if (!File.Exists(alignedApkPath)) { Debug.LogError("apk not found:" + alignedApkPath + ", exec failed:" + buildApkPath); return false; } return true; }
3. 生成补丁压缩包
D:\Projects\UnityProjects\UnityAndroidIl2cppPatchDemo\AndroidGradleProject_v1.1\UnityAndroidIl2cppPatchDemo\src\main\zip_patches.bat
D:\Projects\UnityProjects\UnityAndroidIl2cppPatchDemo\Assets\AndroidIl2cppPatchDemo\PrebuiltPatches\AllAndroidPatchFiles_Version1.zip
[MenuItem("AndroidBuilder/Step 3: Generate Bin Patches", false, 103)] public static bool GenerateBinPatches() { // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.1/UnityAndroidIl2cppPatchDemo/src/main/assets/bin/Data/ string assetBinDataPath = EXPORTED_ASSETS_PATH + "/bin/Data/"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AllAndroidPatchFiles/ string patchTopPath = PROJECT_DIR + "/AllAndroidPatchFiles/"; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AllAndroidPatchFiles//assets_bin_Data/ string assertBinDataPatchPath = patchTopPath + "/assets_bin_Data/"; if (Directory.Exists(patchTopPath)) { FileUtil.DeleteFileOrDirectory(patchTopPath); } Directory.CreateDirectory(assertBinDataPatchPath); string[][] soPatchFile = { // path_in_android_project, filename inside zip, zip file anme new string[3]{ "/"+ SO_DIR_NAME + "/armeabi-v7a/libil2cpp.so", "libil2cpp.so.new", "lib_armeabi-v7a_libil2cpp.so.zip" }, // new string[3]{ "/"+ SO_DIR_NAME + "/x86/libil2cpp.so", "libil2cpp.so.new", "lib_x86_libil2cpp.so.zip" }, //#if UNITY_2018 || UNITY_2019 // new string[3]{ "/"+ SO_DIR_NAME + "/arm64-v8a/libil2cpp.so", "libil2cpp.so.new", "lib_arm64-v8a_libil2cpp.so.zip" }, //#endif }; for (int i = 0; i < soPatchFile.Length; i++) { string[] specialPaths = soPatchFile[i]; // e.g. /jniLibs/armeabi-v7a/libil2cpp.so string projectRelativePath = specialPaths[0]; // e.g. libil2cpp.so.new string pathInZipFile = specialPaths[1]; // e.g. lib_armeabi-v7a_libil2cpp.so.zip string zipFileName = specialPaths[2]; // e.g. D:/Projects/UnityProjects/UnityAndroidIl2cppPatchDemo//AndroidGradleProject_v1.1/UnityAndroidIl2cppPatchDemo/src/main//jniLibs/armeabi-v7a/libil2cpp.so string projectFullPath = BUILD_SCRIPTS_PATH + projectRelativePath; // 将补丁版的 libil2cpp.so 重命名 libil2cpp.so.new 并打包到 为 lib_armeabi-v7a_libil2cpp.so.zip ZipHelper.ZipFile(projectFullPath, pathInZipFile, patchTopPath + zipFileName, 9); } // e.g. 将补丁版的 src/main/assets/bin/Data/ 里的文件 拷贝到 assets_bin_Data 并增加.bin后缀, 文件夹以"_"连接 string[] allAssetsBinDataFiles = Directory.GetFiles(assetBinDataPath, "*", SearchOption.AllDirectories); StringBuilder allZipCmds = new StringBuilder(); allZipCmds.AppendFormat("if not exist \"{0}\" (MD \"{0}\") \n", PROJECT_DIR + "/AllAndroidPatchFiles/"); allZipCmds.AppendFormat("if not exist \"{0}\" (MD \"{0}\") \n", PROJECT_DIR + "/AllAndroidPatchFiles/assets_bin_Data/"); foreach (string apk_file in allAssetsBinDataFiles) { string relativePathHeader = "assets/bin/Data/"; int relativePathStart = apk_file.IndexOf(relativePathHeader); string filenameInZip = apk_file.Substring(relativePathStart); //file: assets/bin/Data/xxx/xxx string relativePath = apk_file.Substring(relativePathStart + relativePathHeader.Length).Replace('\\', '/'); //file: xxx/xxx string zipFileName = relativePath.Replace("/", "__").Replace("\\", "__") + ".bin"; //file: xxx__xxx.bin allZipCmds.AppendFormat("cd {0} && {1} -8 \"{2}\" \"{3}\"\n", BUILD_SCRIPTS_PATH, ZIP_PATH, PROJECT_DIR + "/AllAndroidPatchFiles/assets_bin_Data/" + zipFileName, filenameInZip); } allZipCmds.Append("sleep 1\n"); allZipCmds.AppendFormat("cd {0} && {1} -9 -r \"{2}\" \"{3}\"\n", patchTopPath, ZIP_PATH, PROJECT_DIR + "/AllAndroidPatchFiles_Versionx.zip", "*"); if (allZipCmds.Length > 0) { // 运行批处理 string zipPatchesFile = BUILD_SCRIPTS_PATH + "/" + "zip_patches.bat"; File.WriteAllText(zipPatchesFile, allZipCmds.ToString()); if (!Exec(zipPatchesFile, zipPatchesFile)) { Debug.LogError("exec failed:" + zipPatchesFile); return false; } } return true; }
UnityPlayerActivity.java
package cn.noodle1983.unitypatchdemo; import com.unity3d.player.*; import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.Bundle; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; public class UnityPlayerActivity extends Activity { protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code // Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player // The command line arguments are passed as a string, separated by spaces // UnityPlayerActivity calls this from 'onCreate' // Supported: -force-gles20, -force-gles30, -force-gles31, -force-gles31aep, -force-gles32, -force-gles, -force-vulkan // See https://docs.unity3d.com/Manual/CommandLineArguments.html // @param cmdLine the current command line arguments, may be null // @return the modified command line string or null protected String updateUnityCommandLineArguments(String cmdLine) { return cmdLine; } // Setup activity layout @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity")); getIntent().putExtra("unity", cmdLine); mUnityPlayer = new UnityPlayer(this); setContentView(mUnityPlayer); mUnityPlayer.requestFocus(); } @Override protected void onNewIntent(Intent intent) { // To support deep linking, we need to make sure that the client can get access to // the last sent intent. The clients access this through a JNI api that allows them // to get the intent set on launch. To update that after launch we have to manually // replace the intent with the one caught here. setIntent(intent); } // Quit Unity @Override protected void onDestroy () { mUnityPlayer.destroy(); super.onDestroy(); } // Pause Unity @Override protected void onPause() { super.onPause(); mUnityPlayer.pause(); } // Resume Unity @Override protected void onResume() { super.onResume(); mUnityPlayer.resume(); } @Override protected void onStart() { super.onStart(); mUnityPlayer.start(); } @Override protected void onStop() { super.onStop(); mUnityPlayer.stop(); } // Low Memory Unity @Override public void onLowMemory() { super.onLowMemory(); mUnityPlayer.lowMemory(); } // Trim Memory Unity @Override public void onTrimMemory(int level) { super.onTrimMemory(level); if (level == TRIM_MEMORY_RUNNING_CRITICAL) { mUnityPlayer.lowMemory(); } } // This ensures the layout will be correct. @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mUnityPlayer.configurationChanged(newConfig); } // Notify Unity of the focus change. @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mUnityPlayer.windowFocusChanged(hasFocus); } // For some reason the multiple keyevent type is not supported by the ndk. // Force event injection by overriding dispatchKeyEvent(). @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_MULTIPLE) return mUnityPlayer.injectEvent(event); return super.dispatchKeyEvent(event); } // Pass any events not handled by (unfocused) views straight to UnityPlayer @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } /*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } }
SampleScene测试Gradle文件结构
libs
unity-classes.jar
src
main
assets
bin
Data
Managed
etc
mono
2.0
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
4.0
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
4.5
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
mconfig
config.xml
browscap.ini
config
Metadata
global-metadata.dat
Resources
mscorlib.dll-resources.dat
Resources
unity_builtin_extra
boot.config
globalgamemanagers
globalgamemanagers.assets
level0
sharedassets0.assets
unity default resources
java
com
voidgame
TestForIL2CPP
UnityPlayerActivity.java
jniLibs
armeabi-v7a
libil2cpp.so
libmain.so
libunity.so
res
drawable-xhdpi
app_banner.png
mipmap-anydpi-v26
app_icon.xml
app_icon_round.xml
mipmap-mdpi
app_icon.png
ic_launcher_background.png
ic_launcher_foreground.png
values
strings.xml
styles.xml
values-v21
styles.xml
AndroidManifest.xml
SampleScene测试IL2CPP包文件结构
assets
bin
Data
Managed
etc
mono
2.0
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
4.0
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
4.5
Browsers
Compat.browser
DefaultWsdlHelpGenerator.aspx
machine.config
settings.map
web.config
mconfig
config.xml
browscap.ini
Metadata
global-metadata.dat
Resources
mscorlib.dll-resources.dat
Resources
unity_builtin_extra
boot.config
globalgamemanagers
globalgamemanagers.assets
level0
sharedassets0.assets
unity default resources
lib
armeabi-v7a
libil2cpp.so
libmain.so
libunity.so
META-INF
CERT.RSA
CERT.SF
MANIFEST.MF
res
drawable-xhdpi-v4
app_banner.png
mipmap-anydpi-v26
app_icon.xml
app_icon_round.xml
mipmap-mdpi-v4
app_icon.png
ic_launcher_background.png
ic_launcher_foreground.png
AndroidManifest.xml
classes.dex
resources.arsc
流程图
参考
Git地址: https://github.com/noodle1983/UnityAndroidIl2cppPatchDemo/
libboostrap: https://github.com/noodle1983/UnityAndroidIl2cppPatchDemo-libboostrap
https://docs.unity3d.com/Manual/CommandLineArguments.html
unity_metadata_loader https://github.com/nevermoe/unity_metadata_loader