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

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

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

1.资源访问

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

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

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

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

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

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

接着在代理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插件化框架去深入地学习。

Android插件化开发之动态加载三个关键问题详解相关推荐

  1. Android插件化开发之动态加载本地皮肤包进行换肤

    Android插件化开发之动态加载本地皮肤包进行换肤 前言: 本文主要讲解如何用开源换肤框架 android-skin-loader-lib来实现加载本地皮肤包文件进行换肤,具体可自行参考框架原理进行 ...

  2. Android插件化开发之动态加载技术简单易懂的介绍方式

    转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...

  3. Android插件化开发之动态加载的类型

    https://segmentfault.com/a/1190000005113493 基本信息 Author:kaedea GitHub:android-dynamical-loading 现在网络 ...

  4. Android插件化开发之动态加载技术系列索引

    动态加载介绍 在Android开发中采用动态加载技术,可以达到不安装新的APK就升级APP功能的目的,可以用来到达快速发版的目的,也可以用来修复一些紧急BUG. 现在使用得比较广泛的动态加载技术的核心 ...

  5. Android插件化开发之动态加载基础之ClassLoader工作机制

    类加载器ClassLoader 早期使用过Eclipse等Java编写的软件的同学可能比较熟悉,Eclipse可以加载许多第三方的插件(或者叫扩展),这就是动态加载.这些插件大多是一些Jar包,而使用 ...

  6. Android 插件化开发——宿主APP加载APK插件

    本篇博客说一下我们的宿主APP怎样加载别的APK文件. 首先需要说一些知识点,我们的Java文件要想在Android环境运行,需要将.java文件通过转为class文件,然后为了能在DVM上面运行,再 ...

  7. Android插件化开发实现动态换肤

    今晚实在不想coding,于是想着整理点知识点,那么简单整理了下插件化开发实现动态更换皮肤.插件化开发大家应该不陌生或多或少用过或听过,插件化开发在项目业务拓展.模块化等方面有不小优势,当然实现一个完 ...

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

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  9. Android插件化开发指南——Hook技术(一)【长文】

    文章目录 1. 前言 2. 将外部dex加载到宿主app的dexElements中 3. 插件中四大组件的调用思路 4. Hook 2.1 对startActivity进行Hook 2.1.1 AMS ...

最新文章

  1. 浅谈同一家公司多个系统,共用登录用户名和密码
  2. python 内置函数
  3. debian下安装LNMP环境(一)
  4. 前端学习(3349):数组方法的运用和数值
  5. 深度学习环境搭建之Anaconda安装keras
  6. 距离矢量算法matlab实现,一种基于最小费用距离模型的城市生态网络构建方法与流程...
  7. 这些Java面试题,你一定要记住!
  8. python怎么使用预训练的模型_Tensorflow加载Vgg预训练模型操作
  9. docker 运行 php nginx_使用docker运行nginx
  10. openg离线包_高级openg 混合,一个完整程序
  11. word中软回车和硬回车删除、替换
  12. ChatGPT 被玩挂了,我换成了微信官方对话机器人,简直了....
  13. 用python写一段计算autocad多段线长度的代码
  14. Android开发工具类 Utils
  15. 【js】vue项目中实现点击复制过滤条件,获取并处理粘贴板内容
  16. 计算机网络应用竞赛样题答案,计算机网络技术竞赛选拔赛试题(含答案).doc
  17. mysql的读已提交和可重复读(Read Committed和Repeatable Read隔离级别)
  18. 电子商务计算机网络的定义,网络营销的定义概念是什么
  19. 发电机组工作安排问题
  20. mac安装monkey工具

热门文章

  1. 上几个WebAPI就算微服务架构?Too Young!
  2. TensorFlow 2学习和工业CV领域应用 心得分享
  3. dotnet core 通过 frp 发布自己的网站
  4. VSCode开发.NETCore项目入门(1)设置中文语言环境
  5. asp.netcore3.0 使用 DbProviderFactories 连接数据库
  6. Apollo 配置中心:分布式部署
  7. 自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
  8. .NET Core中的验证组件FluentValidation的实战分享
  9. 分布式系统搭建:服务发现揭秘
  10. 程序员小测试:保守派 vs 自由派