Android动态加载技术三个关键问题详解

本文摘选自任玉刚著《Android开发艺术探索》,介绍了Android插件化技术的原理和三个关键问题,并给出了做者本身发起的开源插件化框架。git

动态加载技术(也叫插件化技术)在技术驱动型的公司中扮演着至关重要的角色,当项目愈来愈庞大的时候,须要经过插件化来减轻应用的内存和CPU占 用,还能够实现热插拔,即在不发布新版本的状况下更新某些模块。动态加载是一项很复杂的技术,这里主要介绍动态加载技术中的三个基础性问题,至于完整的动态加载技术的实现请参考笔者发起的开源插件化框架DL(https://github.com/singwhatiwanna/dynamic-load-apk)。项目期间有多位开发人员一块儿贡献代码。github

不一样的插件化方案各有各的特点,可是它们都必需要解决三个基础性问题:资源访问、Activity生命周期的管理和ClassLoader的管理。 在介绍它们以前,首先要明白宿主和插件的概念,宿主是指普通的apk,而插件通常是指通过处理的dex或者apk,在主流的插件化框架中多采用通过特殊处 理的apk来做为插件,处理方式每每和编译以及打包环节有关,另外不少插件化框架都须要用到代理Activity的概念,插件Activity的启动大多 数是借助一个代理Activity来实现的。框架

1.资源访问性能

咱们知道,宿主程序调起未安装的插件apk,一个很大的问题就是资源如何访问,具体来讲就是插件中凡是以R开头的资源都不能访问了。这是由于宿主程序中并无插件的资源,因此经过R来加载插件的资源是行不通的,程序会抛出异常:没法找到某某id所对应的资源。学习

针对这个问题,有人提出了将插件中的资源在宿主程序中也预置一份,这虽然能解决问题,可是这样就会产生一些弊端。首先,这样就须要宿主和插件同时持 有一份相同的资源,增长了宿主apk的大小;其次,在这种模式下,每次发布一个插件都须要将资源复制到宿主程序中,这意味着每发布一个插件都要更新一下宿 主程序,这就和插件化的思想相违背了。大数据

由于插件化的目的就是要减少宿主程序apk包的大小,同时下降宿主程序的更新频率并作到自由装载模块,因此这种方法不可取,它限制了插件的线上更新 这一重要特性。还有人提供了另外一种方式,首先将插件中的资源解压出来,而后经过文件流去读取资源,这样作理论上是可行的,可是实际操做起来仍是有很大难度 的。首先不一样资源有不一样的文件流格式,好比图片、XML等,其次针对不一样设备加载的资源多是不同的,如何选择合适的资源也是一个须要解决的问题,基于这两点,这种方法也不建议使用,由于它实现起来有较大难度。为了方便地对插件进行资源管理,下面给出一种合理的方式。spa

咱们知道,Activity的工做主要是经过ContextImpl来完成的, Activity中有一个叫mBase的成员变量,它的类型就是ContextImpl。注意到Context中有以下两个抽象方法,看起来是和资源有关 的,实际上Context就是经过它们来获取资源的。这两个抽象方法的真正实如今ContextImpl中,也就是说,只要实现这两个方法,就能够解决资源问题了。插件

下面给出具体的实现方式,首先要加载apk中的资源,以下所示。代理

从loadResources()的实现能够看出,加载资源的方法是经过反射,经过调用AssetManager中的addAssetPath方 法,咱们能够将一个apk中的资源加载到Resources对象中,因为addAssetPath是隐藏API咱们没法直接调用,因此只能经过反射。下面 是它的声明,经过注释咱们能够看出,传递的路径能够是zip文件也能够是一个资源目录,而apk就是一个zip,因此直接将apk的路径传给它,资源就加 载到AssetManager中了。而后再经过AssetManager来建立一个新的Resources对象,经过这个对象咱们就能够访问插件apk中 的资源了,这样一来问题就解决了。orm

接着在代理Activity中实现getAssets()和getResources(),以下所示。关于代理Activity的含义请参看DL开源插件化框架的实现细节,这里再也不详细描述了。

经过上述这两个步骤,就能够经过R来访问插件中的资源了。

2.Activity生命周期的管理

管理Activity生命周期的方式各类各样,这里只介绍两种:反射方式和接口方式。反射的方式很好理解,首先经过Java的反射去获取 Activity的各类生命周期方法,好比onCreate、onStart、onResume等,而后在代理Activity中去调用插件 Activity对应的生命周期方法便可,以下所示。

使用反射来管理插件Activity的生命周期是有缺点的,一方面是反射代码写起来比较复杂,另外一方面是过多使用反射会有必定的性能开销。下面介绍 接口方式,接口方式很好地解决了反射方式的不足之处,这种方式将Activity的生命周期方法提取出来做为一个接口(好比叫DLPlugin),而后通 过代理Activity去调用插件Activity的生命周期方法,这样就完成了插件Activity的生命周期管理,而且没有采用反射,这就解决了性能 问题。同时接口的声明也比较简单,下面是DLPlugin的声明:

在代理Activity中只须要按以下方式便可调用插件Activity的生命周期方法,这就完成了插件Activity的生命周期的管理。

经过上述代码应该不难理解接口方式对插件Activity生命周期的管理思想,其中mRemoteActivity就是DLPlugin的实现。

3.插件ClassLoader的管理

为了更好地对多插件进行支持,须要合理地去管理各个插件的DexClassLoader,这样同一个插件就能够采用同一个ClassLoader去 加载类,从而避免了多个ClassLoader加载同一个类时所引起的类型转换错误。在下面的代码中,经过将不一样插件的ClassLoader存储在一个 HashMap中,这样就能够保证不一样插件中的类彼此互不干扰。

事实上插件化的技术细节很是多,这绝非一个章节的内容所能描述清楚的,另外插件化做为一种核心技术,须要开发者有较深的开发功底才可以很好地理解, 所以本节的内容更可能是让读者对插件化开发有一个感性的了解,细节上还须要读者本身去钻研,也能够经过DL插件化框架去深刻地学习。


阿里百川(baichuan.taobao.com)是阿里巴巴集团的无线开放平台,经过“技术、商业及大数据”的开放,提供移动场景下的高内聚、开放式、行业领先的技术产品矩阵、成熟的商业组件和完善的服务体系,帮助移动开发者快速搭建APP、加速APP商业化进程,全方位赋能移动开发者及移动创业者。

相关文章
相关标签/搜索