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;
    }
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}\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;
    }
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:\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;
    }
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

 

posted on 2020-07-06 11:03  void87  阅读(1201)  评论(0编辑  收藏  举报

导航