热文导读 | 点击标题阅读

金九银十跳槽季如何进阶找到合适满意的工作?

身为程序员碰到最奇葩的需求是怎样的?

2018 年 8 月面试路:6 天 21 家公司

作者:*家伟

来源:https://zhuanlan.zhihu.com/p/36902641

Android App Bundles

Android App Bundles(以下简称AAB)是今年Google I/O大会带来的一款全新动态化框架,与Instant App不同,AAB是借助Split Apk完成动态加载。介绍AAB之前,先来了解下SplitApk。

Split Apks

split apks是Android 5.0开始提供多apk构建机制,借助split apks可以将一个apk基于ABI和屏幕密度两个维度拆分城多个apk,这样可以有效减少apk体积。当用户下载应用程序安装包时,只会包含对应平台的so和资源。因为需要google play支持,所以国内就没戏了。针对不同cpu架构问题,国内应用开发商大部分都会将so文件只放在armabi目录下,如此做虽然可以有效减少包体积,但可能带来性能问题。


安装应用程序时,首先安装base apk,然后安装split apks。为了解splite apks运作原理,我们还是结合源码做简要分析。因为splite apks是Android 5.0开始支持,所以我们以5.0版本开始分析。

在爱奇艺组件化探索之原理篇文中有介绍相关动态化知识,因此本文不再赘述。

在ApplicationInfo中,增加splites apk相关字段。

/*** Full paths to zero or more split APKs that, when combined with the base* APK defined in {@link #sourceDir}, form a complete application.*/public String[] splitSourceDirs;/*** Full path to the publicly available parts of {@link #splitSourceDirs},* including resources and manifest. This may be different from* {@link #splitSourceDirs} if an application is forward locked.*/public String[] splitPublicSourceDirs;

LoadeApk中有PathClassLoader和Resources创建过程。LoadedApk#mClassLoader是PathClassLoader实例引用,接着分析PathClassLoader创建过程。

public ClassLoader getClassLoader() {synchronized (this) {if (mClassLoader != null) {return mClassLoader;}if (mIncludeCode && !mPackageName.equals("android")) {......final ArrayList<String> 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;}}

在创建PathClassLoader时,dex文件路径包含base app和split apps路径。LoadedApk#mResources是Resources实例引用,其创建过程如下。

public Resources getResources(ActivityThread mainThread) {if (mResources == null) {mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);}return mResources;}

该方法中,split apks资源路径(LoadedApk#mSplitResDirs)也会被增加至Resources中。

以上简要介绍split apks加载过程,包括code和resources加载。split apks并不支持动态加载split apk,即base apk 和split apks在app安装时,全部安装。但通过split apks工作原理,可以发现其是能够支持按需加载。

初识Android App Bundles

针对split apks的不足,Google今年在其I/O大会上推出AAB,AAB提供动态安装apk功能,这样可以进一步减少apk体积,首先来看张图。

很遗憾,AAB需要google play支持,国内开发者依旧无缘。


从上图可以看出,AAB较split apks多出on-demand模式。AAB也是基于spilt apks来完成按需加载功能,split apks安装需要借助google play来完成。

  • Base APK: 当用户下载你的app时,base apk是首个被安装,所有split apks都能访问base apk代码和资源。

  • Configuration APKs: 这些apks包含特定屏幕密度、CPU架构so文件、语言。当下载base或者feature apk时,google play会根据当前设备特征下载指定configuration apks。

  • Dynamic feature APKs: 这些apks包括代码和资源,当app首次安装时,它们不会被安装,在用户需要使用该feature功能时才会被加载。

更多关于AAB说明,大家可以阅读官方文档。下载最新Preview release版Android Studio和AAB Sample,接着我们根据官方Samples来看看如何玩转AAB。

AAB功能介绍

AAB并不是一个插件化框架,它是利用Android Framework提供的split apks功能完成。所有安装split apk工作均是通过IPC交由google play完成。(第三方app是没有安装split apk能力,只有platform签名应用才行,或者被Root手机)。

Dynamic Feature Module

Android Studio新增一项module——Dynamic Feature Module。


在创建dynamic_feature时,有两个选项是默认勾选的,当然我们也可以更改其状态。


  • Enable on-demand: 是否支持按需下载模式。如果不支持,那么该feature则在安装app时被安装。

  • Fusing: 如果app运行在Android 5.0(不包括5.0)以下,勾选Fusing则表示该feature会被一起打包至完整apk中。

接着分析AAB示例。

在示例中,有四个feature,通过module名很清楚这些feature是举例介绍如何访问代码、资源、so等。

dynamic feature module编译所使用的插件com.android.dynamic-feature,那么该插件有何独特之处。我们通过编译产物分析,运行示例后,发现在所有dynamic feature模块build目录下均会生成apk文件。com.android.dynamic-feature独特之处编译目标是apk文件。

接着反编译主apk(com.android.application插件生成产物),发现:

  • 所有dynamic feature module的代码、资源、so并未打包至主apk中。

  • 主apk manifest信息包括所有dynamic feature module的manifest,即feature manifest会被合并至主apk manifest中。


Build Bundle(s)

Android App Bundle提供一种全新编译产物格式文件aab。


如上图,当选择Build Bundle(s)时,在主工程build目录下回生成bundle.aab文件,该文件是压缩格式文件,因此将其解压分析。


从aab文件内容,可知其包含base和feature的代码、资源、so等,同时还有BundleConfig.pb这一配置文件,该配置文件是google play用于拆分apk。如果我们需要在google play上支持动态发布,只需要上传aab文件即可,后续工作交给google play完成。

另外需要注意的是,如果app运行在4.4及以下设备,则用户下载的将是完成的apk文件,不支持任何split apks特性。


上图是AAB文档提供的一张关于aab文件结构图,蓝色方框区域就是configuration apks支持的配置项。

在之前split apks打包中,是不支持语言配置。

以上简要介绍了AAB相关知识点,如有兴趣深入了解还是查阅官方文档。

Play Core Library

Play Core Library是AAB提供的核心库,用于下载、安装dynamic feature模块。另外,我们也可以用这些API下载on-demand模块用于instant app。

关于Play Core Library具体如何使用,大家看下文档,本文主要讲解一些原理性知识点。

主工程模块app,首先分析MainActivity.kt文件。该类是用kotlin编写,如果没有接触过不要紧,大体上还是能看懂。

在MainActivity.kt的onCreate方法中,增加如下逻辑:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)manager = SplitInstallManagerFactory.create(this)initializeViews()val installedList = manager.installedModules.toList()for (item in installedList) {toastAndLog("installed module : " + item.toString())}val splits = applicationInfo.splitSourceDirsfor ( item in splits) {toastAndLog("split dir : " + item.toString())}}

打印结果如下:

D/DynamicFeatures: installed module : native
D/DynamicFeatures: installed module : java
D/DynamicFeatures: installed module : kotlin
D/DynamicFeatures: installed module : assets
D/DynamicFeatures: split dir : /data/app/com.google.android.samples.dynamicapps.ondemand-1/split_assets.apk
D/DynamicFeatures: split dir : /data/app/com.google.android.samples.dynamicapps.ondemand-1/split_java.apk
D/DynamicFeatures: split dir : /data/app/com.google.android.samples.dynamicapps.ondemand-1/split_kotlin.apk
D/DynamicFeatures: split dir : /data/app/com.google.android.samples.dynamicapps.ondemand-1/split_native.apk

从运行结果可知,split apks(即使是on-demand模块)在debug模式下,是紧接着base apk安装完成后安装。

SplitInstallManager类提供获取已安装模块方法。

Set<String> getInstalledModules();

因为Play Core Library非对外暴露接口都是混淆过的,因此就不直接附源码分析。但通过追踪分析源码可知,获取已安装模块的核心过程是:

private final String[] a() {try {PackageInfo var1;return (var1 = this.d.getPackageManager().getPackageInfo(this.e, 0)) != null ? var1.splitNames : null;} catch (NameNotFoundException var2) {a.c("App is not found in PackageManager", new Object[0]);return null;}}

通过PackageInfo#splitNames字段获取。

在示例中,每当我们需要启动dynamic feature模块时,都要判断该模块是否安装。如果没有安装,则启动下载,Play Core Library提供了比较完善的下载状态回调,比如下载进度,下载失败原因等等。

通过粗略分析这些混淆源码可知,下载与安装on-demand模块均是通过ipc交由google play完成。

兼容性问题

OS版本不高于6.0

当app运行设备版本不高于6.0时,需要使用SplitCompat库才能立即访问下载模块代码和资源。AAB提供SplitCompatApplication类用于开启SplitCompat。

public class SplitCompatApplication extends Application {public SplitCompatApplication() {}protected void attachBaseContext(Context var1) {super.attachBaseContext(var1);SplitCompat.install(this);}
}

Application#attachBaseContext(Context)中调用SplitCompat.install(Context)。在该方法中主要完成split apks代码(dex和so)和资源的安装。

因为代码都是混淆过的,因此只能大概知道SplitCompat做了哪些操作。在SplitCompat#a(boolean)方法调用了
com.google.android.play.core.splitcompat.b.b.a()方法,其中有对不同版本OS兼容性处理。

public static a a() {        if (VERSION.SDK_INT == 21) {        //com.google.android.play.core.splitcompat.b.c            return new c();        } else if (VERSION.SDK_INT == 22) {        //com.google.android.play.core.splitcompat.b.f            return new f();        } else if (VERSION.SDK_INT == 23) {        //com.google.android.play.core.splitcompat.b.g            return new g();        } else {            throw new AssertionError();        }    }

分别查看com.google.android.play.core.splitcompat.b.c、com.google.android.play.core.splitcompat.b.f、com.google.android.play.core.splitcompat.b.g,得知其主要做so加载和dex加载(dex前插,与mutil-dex类似)。split apks资源加载在SplitCompat#a(boolean)方法有反射调用AssetManager#addAssetPath(String)

OS版本不低于8.0

在Android 8.0中,Instant Apps相关代码嵌入至Framework。因此如果on-demand模块用于Instant Apps中,需要在on-demand下载成功中,调用SplitInstallHelper.updateAppInfo(Context)

public static void updateAppInfo(Context var0) {        if (VERSION.SDK_INT > 25) {            a.a("Calling dispatchPackageBroadcast!", new Object[0]);            try {                Class var1;                Method var2;                (var2 = (var1 = Class.forName("android.app.ActivityThread")).getMethod("currentActivityThread")).setAccessible(true);                Object var3 = var2.invoke((Object)null);                Field var4;                (var4 = var1.getDeclaredField("mAppThread")).setAccessible(true);                Object var5;                (var5 = var4.get(var3)).getClass().getMethod("dispatchPackageBroadcast", Integer.TYPE, String[].class).invoke(var5, 3, new String[]{var0.getPackageName()});                a.a("Calling dispatchPackageBroadcast", new Object[0]);            } catch (Exception var6) {                a.a(var6, "Update app info with dispatchPackageBroadcast failed!", new Object[0]);            }        }    }

从上述代码得知其反射调用ActivityThread#dispatchPackageBroadcast方法。最终是调用至LoadedApk#updateApplicationInfo。该方法做了如下事情

  • 重新创建mClassLoader

  • 重新创建mResources

  • 更新applicationInfo(调用LoadedApk#setApplicationInfo完成)。

加载C/C++库

Play Core库提供SplitInstallHelper#loadLibrary用于加载C/C++库。具体用法可以查看示例。

结语

Android App Bundles的出现,相当是官方提供的一套动态化框架,所有的on-demand模块也会被google play审核,这对用户来说是非常有利的。

你有什么想说的?欢迎留言区留言讨论!

你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可


最后,欢迎大家加入我们的知识星球,这期是到2019年3月10日结束,所以越早加入越好,现在加入的球友快1000人了,到1000人时将大幅提价(还有最后几十个名额了),所以快上车!

微信扫描或者点击上方二维码领取Android\Python\AI\Java等高级进阶资源

更多学习资料点击下面的“阅读原文”获取

Google官方插件化解决方案—全新的动态化框架Android App Bundles分析 Seasoninthesun相关推荐

  1. Android App Bundles(谷歌官方动态化框架)

    提纲 是什么(Android App Bundles简介) 为什么使用(aab的四个新功能及实用性) 怎么使用(怎么进行aab的改造) 怎么测试(可以通过google市场测试 而本文主要介绍aab本地 ...

  2. 是时候来一波Android插件化了

    是时候来一波Android插件化了 是时候来一波Android插件化了 前言 Android开发演进 模块化介绍 插件化介绍 前提技术介绍 APK构成 Manifest Application 四大组 ...

  3. 携程Android App插件化和动态加载实践

    转载自:http://www.infoq.com/cn/articles/ctrip-android-dynamic-loading?email=947091870@qq.com 编者按:本文为携程无 ...

  4. 58同城Android端-最小插件化框架实战和原理分析

    目录 背景 插件化需要了解的知识 2.1 类加载过程和类加载器 2.2 ClassLoader 的 findClass.findLibrary.findResource 2.3 DexClassLoa ...

  5. 美团App 插件化实践

    背景 在Android开发行业里,插件化已经不是一门新鲜的技术了,在稍大的平台型App上早已是标配.进入2017年,Atlas.Replugin.VirtualAPK相继开源,标志着插件化技术进入了成 ...

  6. 美团App插件化实践

    点击上方"公众号"可以订阅哦 背景 在Android开发行业里,插件化已经不是一门新鲜的技术了,在稍大的平台型App上早已是标配.进入2017年,Atlas.Replugin.Vi ...

  7. 【Android 插件化】插件化原理 ( 类加载器 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  8. (4.6.29.3)插件化之代码加载:启动Activity等四大组件之hook方式

    文章目录 一.代理模式和Hook原理 1.1 Hook 原理 1.2 代理模式 二.Binder Hook 2.1 分析:系统服务的获取过程 2.2 寻找Hook点 2.3 hook Binder示例 ...

  9. 插件化知识储备-Binder和AIDL原理

    前言 插件化技术火热已久,为什么会有插件化,时势造英雄吧,随着移动互联网的快速发展,业务的飞速增长,如何在有限时间给用户提供高质量的APP,当线上出现各种BUG,如何快速修复并发布上线,插件化的意义也 ...

最新文章

  1. 关于MongDB数据迁移方案的研究
  2. android蓝牙串口通讯
  3. 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础
  4. 遗落在时光里的静态链表(线性表的静态存储)---C语言版
  5. Picture Control控件图象保存为bmp,jpg,emf,tif,gif
  6. Microsoft Dynamics AX 2012
  7. java实现递归算法
  8. 【IIS】修改注册表键值提升IIS的性能
  9. Python基础+数据科学入门(四)程序控制结构
  10. opencv分量法、加权平均法、最大值法、平均值法灰度化
  11. python绘制爱心气球_婚房布置气球爱心教程-婚房气球布置心形方法简单【蜜匠婚礼】...
  12. 服务器显示器超分辨率,显示器分辨率超频1080超到2K屏方法
  13. Shader 常用函数
  14. Luat模块应用手册-指南-Luat二次开发教程指南-功能开发教程-长连接超低功耗方案
  15. 中国象棋游戏Chess(3) - 实现走棋规则
  16. 第八届蓝桥杯个人赛赛后总结
  17. 关于预答辩研究生汇报内容及方式的几点要求
  18. 政府OA办公系统实施时需要关注的五个环节
  19. 从Python到TensorFlow,差点把我 六年的电脑砸了,哈哈哈哈(详细安装入门步骤)
  20. C# Winform 计算机原理模型机的设计——带超前进位加法器

热门文章

  1. Beta冲刺提交—星期五
  2. 一般邮箱的邮件群发数量限制是多少?
  3. 怎么修改邮箱服务器类型,邮箱登录手动修改服务器配置
  4. vue报错 运行npm run dev报cjs.js缺失
  5. EndNote使用手册(无图版)
  6. 计算机未检测到无线接收器,wifi接收器插上没反应 wifi接收器插上没反应怎么办 - 云骑士一键重装系统...
  7. 评价计算机主机电源的标准主要有哪些,PC电源好不好?这项参数很重要
  8. C语言系统功能的描述,C语言20操作系统介绍ppt课件
  9. 物联网+大数据+云计算+人工智能
  10. 对“判断内六角规格项目”一些改进与完善