摘要: Android App Bundles 在今年的Google I/O大会上,Google向 Android 引入了新 App 动态化框架(即Android App Bundle,缩写为AAB),与Instant App不同,AAB是借助Split Apk完成动态加载,使用AAB动态下发方式,可以大幅度减少应用体积。
## Android App Bundles 在今年的Google I/O大会上,Google向 Android 引入了新 App 动态化框架(即Android App Bundle,缩写为AAB),与Instant App不同,AAB是借助Split Apk完成动态加载,使用AAB动态下发方式,可以大幅度减少应用体积。现在只须在 Android Studio 中构建一个应用束 (app bundle),就可以将应用所需的全部内容 (适用于所有设备) 都涵盖在内:所有语言、所有设备屏幕大小、所有硬件架构。 下面是Dynamic Delivery示意效果图: 不过要想体验Dynamic Delivery,需要先下载 Android Studio 3.2 学习Android App Bundles可以将它和Split Apks来对比学习。 Split Apks split apks是Android 5.0开始提供多apk构建机制,借助split apks可以将一个apk基于ABI和屏幕密度两个维度拆分城多个apk,这样可以有效减少apk体积。当用户下载应用程序安装包时,只会包含对应平台的so和资源。因为需要google play支持,所以国内就没戏了。针对不同cpu架构问题,国内应用开发商大部分都会将so文件只放在armabi目录下,如此做虽然可以有效减少包体积,但可能带来性能问题。split apks详细的内容可以访问下面的链接:[https://link.zhihu.com/?target=https%3A//developer.android.com/studio/build/configure-apk-splits%3Fauthuser%3D2](https://link.zhihu.com/?target=https%3A//developer.android.com/studio/build/configure-apk-splits%3Fauthuser%3D2) Split Apks的运作原理有点类似于Android的组件化,安装应用程序时,首先安装base apk,然后安装split apks。为了说明splite apks运作原理,来看一下Android 5.0关于splite apks的源码。 打开[ApplicationInfo](http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/java/android/content/pm/ApplicationInfo.java)类中,可以看到如下信息: `/** * Full paths to zero <span class="hljs-keyword">or</span> more <span class="hljs-keyword">split</span> APKs that, <span class="hljs-keyword">when</span> combined with the base * APK <span class="hljs-keyword">defined</span> in {@link <span class="hljs-comment">#sourceDir}, form a complete application.</span> *<span class="hljs-regexp">/ public String[] splitSourceDirs; /</span>** * Full path to the publicly available parts of {@link <span class="hljs-comment">#splitSourceDirs},</span> * including resources <span class="hljs-keyword">and</span> manifest. This may be different from * {@link <span class="hljs-comment">#splitSourceDirs} if an application is forward locked.</span> *<span class="hljs-regexp">/ public String[] splitPublicSourceDirs;</span>` [LoadeApk](http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/java/android/app/LoadedApk.java)中有PathClassLoader和Resources创建过程。LoadedApk#mClassLoader是PathClassLoader实例引用,接着看PathClassLoader的创建过程。 zipPaths = new ArrayList<>(); final ArrayList<String> libPaths = new ArrayList<>(); ....... zipPaths.add(mAppDir); //将split apk路径追加到zipPaths中 if (mSplitAppDirs != null) { Collections.addAll(zipPaths, mSplitAppDirs); } libPaths.add(mLibDir); ...... final String zip = TextUtils.join(File.pathSeparator, zipPaths); final String lib = TextUtils.join(File.pathSeparator, libPaths); ...... //如果mSplitAppDirs不为空,则zip将包含split apps所有路径。 mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); } else { if (mBaseClassLoader == null) { mClassLoader = ClassLoader.getSystemClassLoader(); } else { mClassLoader = mBaseClassLoader; } } return mClassLoader; } }" data-snippet-id="ext.b0abcb7003a3d76aeb1ae260fba2afe7" data-snippet-saved="false" data-codota-status="done">`<span class="hljs-function"><span class="hljs-keyword">public</span> ClassLoader <span class="hljs-title">getClassLoader</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) { <span class="hljs-keyword">if</span> (mClassLoader != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">return</span> mClassLoader; } <span class="hljs-keyword">if</span> (mIncludeCode && !mPackageName.equals(<span class="hljs-string">"android"</span>)) { ...... <span class="hljs-keyword">final</span> ArrayList<String> zipPaths = <span class="hljs-keyword">new</span> ArrayList<>(); <span class="hljs-keyword">final</span> ArrayList<String> libPaths = <span class="hljs-keyword">new</span> ArrayList<>(); ....... zipPaths.add(mAppDir); <span class="hljs-comment">//将split apk路径追加到zipPaths中</span> <span class="hljs-keyword">if</span> (mSplitAppDirs != <span class="hljs-keyword">null</span>) { Collections.addAll(zipPaths, mSplitAppDirs); } libPaths.add(mLibDir); ...... <span class="hljs-keyword">final</span> String zip = TextUtils.join(File.pathSeparator, zipPaths); <span class="hljs-keyword">final</span> String lib = TextUtils.join(File.pathSeparator, libPaths); ...... <span class="hljs-comment">//如果mSplitAppDirs不为空,则zip将包含split apps所有路径。</span> mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">if</span> (mBaseClassLoader == <span class="hljs-keyword">null</span>) { mClassLoader = ClassLoader.getSystemClassLoader(); } <span class="hljs-keyword">else</span> { mClassLoader = mBaseClassLoader; } } <span class="hljs-keyword">return</span> mClassLoader; } }` 在创建PathClassLoader时,dex文件路径包含base app和split apps路径,LoadedApk#mResources是Resources实例引用,Resources的源码如下: `<span class="hljs-function"><span class="hljs-keyword">public</span> Resources <span class="hljs-title">getResources</span>(<span class="hljs-params">ActivityThread mainThread</span>) </span>{ <span class="hljs-keyword">if</span> (mResources == <span class="hljs-literal">null</span>) { mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, <span class="hljs-literal">null</span>, <span class="hljs-keyword">this</span>); } <span class="hljs-keyword">return</span> mResources; }` 可以发现:split apks资源路径(LoadedApk#mSplitResDirs)也会被增加至Resources中。 Android App Bundles 下面再来看Android App Bundles,Android App Bundle 支持模块化,通过Dynamic Delivery with split APKs,将一个apk拆分成多个apk,按需加载(包括加载C/C++ libraries),这样开发者可以随时按需交付功能,而不是仅限在安装过程中。 Android App Bundle 通常会包括以下几个文件: - Base Apk:首次安装的apk,公共代码和资源,所以其他的模块都基于Base Apk; - Configuration APKs:native libraries 和适配当前手机屏幕分辨率的资源; - Dynamic feature APKs:不需要在首次安装就加载的模块。  AAB并不是一个插件化框架,它利用的是Android Framework提供的split apks技术来完成的,而所有安装split apk工作均是通过IPC交由google play完成。 具体使用时,在Android Studio新增一项module——Dynamic Feature Module。 在创建dynamic_feature时,有两个选项是默认勾选的,当然我们也可以更改其状态。 ...