zoukankan      html  css  js  c++  java
  • Unity Android APP il2cpp 学习

    相关设置

    AssetsAndroidIl2cppPatchDemoAndroidBuilder.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;
        }
    ValidateConfig

      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;
        }
    ExportGradleProject

      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;
        }
    PatchAndroidProject

      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}
    
    ", 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}
    
    ", 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}
    
    @echo on
    
    ", 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}
    
    ", 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}
    
    ", 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}
    
    ", apksignerPath, signParam);
    
            //del tmp apk
            allCmd.AppendFormat("del /f /a /Q {0}
    
    ", unaligned_apk_path.Replace("/", "\"));
            allCmd.AppendFormat("explorer.exe {0} 
    
    ", binPath.Replace("//", "/").Replace("/", "\"));
            allCmd.AppendFormat("@echo on
    
    "); //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;
        }
    GenerateBuildScripts

      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;
        }
    BuildApk

      3. 生成补丁压缩包
        D:ProjectsUnityProjectsUnityAndroidIl2cppPatchDemoAndroidGradleProject_v1.1UnityAndroidIl2cppPatchDemosrcmainzip_patches.bat
        D:ProjectsUnityProjectsUnityAndroidIl2cppPatchDemoAssetsAndroidIl2cppPatchDemoPrebuiltPatchesAllAndroidPatchFiles_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}") 
    ", PROJECT_DIR + "/AllAndroidPatchFiles/");
            allZipCmds.AppendFormat("if not exist "{0}" (MD "{0}") 
    ", 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}"
    ", BUILD_SCRIPTS_PATH, ZIP_PATH, PROJECT_DIR + "/AllAndroidPatchFiles/assets_bin_Data/" + zipFileName, filenameInZip);
            }
            allZipCmds.Append("sleep 1
    ");
            allZipCmds.AppendFormat("cd {0} && {1} -9 -r "{2}" "{3}"
    ", 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;
        }
    GenerateBinPatches

    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); }
    }
    UnityPlayerActivity.java

    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

  • 相关阅读:
    Asp.net 动态添加Meta标签
    【转】在SharePoint Server 2010中更改“我的网站”
    SPQuery DateTime 类型查询
    Asp.net Web Application 打开 SharePoint 2010 Site 错误 The Web application at could not be found
    How To Create SharePoint 2010 Site Collection In Its Own DB
    C# 文件打印
    面试题 java集合
    《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读
    《深入理解Java虚拟机》(五)JVM调优
    《深入理解Java虚拟机》(四)虚拟机性能监控与故障处理工具
  • 原文地址:https://www.cnblogs.com/revoid/p/13253742.html
Copyright © 2011-2022 走看看