Skip to content

释放Apk

canaan edited this page Sep 1, 2016 · 1 revision

当这是新的补丁包时,首先第一件事就是释放。ApkReleaser.work(this, layoutId, themeId)在这个方法中最终会去开启一个 ApkReleaseActivity,而这个 Activity 的layout 和 theme 就是之前从配置中解析出来,在 work 方法中传进来的layoutId 和 themeId。

ApkReleaseActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
	...

   new Thread() {
       @Override
       public void run() {
           super.run();

           DexReleaser.releaseDexes(demoAPk.getAbsolutePath(), dexDir.getAbsolutePath());
           NativeLibraryHelperCompat.copyNativeBinaries(demoAPk, nativeLibraryDir);
           dexOptimization();

           handler.sendEmptyMessage(WHAT_DEX_OPT_DONE);
       }
   }.start();
}

在 ApkReleaseActivity 的 onCreate() 方法中会开启一个线程去进行一系列的释放操作,这些操作十分耗时,目前在不同的机子上测试,从几秒到二十几秒之间不等,如果就这样黑屏在用户前面未免太不优雅,所以 Amigo 开启了一个新的进程,启动这个 Activity。 在这个线程中,做了三件微小的事情:

  • 释放 Dex 到指定目录

  • 拷贝 so 文件到 Amigo 的指定目录下 拷贝 so 文件是通过反射去调用 NativeLibraryHelper这个类的nativeCopyNativeBinaries()方法,但这个方法在不同版本上有不同的实现。

    • 如果版本号在21以下

      NativeLibraryHelper

       public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) {
           final String cpuAbi = Build.CPU_ABI;
           final String cpuAbi2 = Build.CPU_ABI2;
           return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi,
                   cpuAbi2);
       }
      

      会去反射调用这个方法,其中系统会自动判断出 primaryAbi 和 secondAbi。

    • 如果版本号在21以上 copyNativeBinariesIfNeededLI(file, file)这个方法已经被废弃了,需要去反射调用这个方法

      NativeLibraryHelper

       public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
           for (long apkHandle : handle.apkHandles) {
               int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
                       handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
               if (res != INSTALL_SUCCEEDED) {
                   return res;
               }
           }
           return INSTALL_SUCCEEDED;
       }
      

      所以首先得去获得一个NativeLibraryHelper$Handle类的实例。之后就是找 primaryAbi。Amigo 先对机器的位数做了判断,如果是64位的机子,就只找64位的 abi,如果是32位的,就只找32位的 abi。然后将 Handle 实例当做参数去调用NativeLibraryHelperfindSupportedAbi来获得primaryAbi。最后再去调用copyNativeBinaries去拷贝 so 文件。

    对于 so 文件加载的原理可以参考这篇文章

  • 优化 dex 文件

    ApkReleaseActivity.java

     private void dexOptimization() {
     	...
         for (File dex : validDexes) {
             new DexClassLoader(dex.getAbsolutePath(), optimizedDir.getAbsolutePath(), null, DexUtils.getPathClassLoader());
             Log.e(TAG, "dexOptimization finished-->" + dex);
         }
     }

    DexClassLoader 没有做什么事情,只是调用了父类构造器,他的父类是 BaseDexClassLoader。在 BaseDexClassLoader 的构造器中又去构造了一个DexPathList 对象。 在DexPathList类中,有一个 Element 数组

    DexPathList

     /** list of dex/resource (class path) elements */
     private final Element[] dexElements;
    

    Element 就是对 Dex 的封装。所以一个 Element 对应一个 Dex。这个 Element 在后文中会提到。

    优化 dex 只需要在构造 DexClassLoader 对象的时候将 dex 的路径传进去,系统会在最后会通过DexFile

    DexFile.java

    native private static int openDexFile(String sourceName, String outputName,
          int flags) throws IOException;

    来这个方法来加载 dex,加载的同时会对其做优化处理。

这三项操作完成之后,通知优化完毕,之后就关闭这个进程,将补丁包的校验和保存下来。这样第一步释放 Apk 就完成了。

Clone this wiki locally