-
Notifications
You must be signed in to change notification settings - Fork 213
释放Apk
当这是新的补丁包时,首先第一件事就是释放。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 实例当做参数去调用NativeLibraryHelper
的findSupportedAbi
来获得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 就完成了。