不长不短的职业生涯里,有一段搞插件化的经历,其时所在的团队也是行业里比较知名的最早搞插件化的团队之一。虽然理论上是运用方,但由于业务的需求,要把大插件拆成更小颗粒度的小插件,所以会比较深度的做源码级别的定制修改。

1 什么是插件化

插件化要处理的问题总的来说有三个方面

  • 动态性:也便是更新功用无需依靠发版,动态下发应用的新功用。
  • 包体积:一个巨型的APP功用模块很多,包体积自然小不了。插件化能够把不同的功用模块制作成独自的插件,按需下载应用,有用控制包体积。同时,关于一些“边缘功用”,关于每个用户个体来说或许,运用不到,插件化按需下载的优势也就体现出来了。
  • 热修复: 关于线上的bug,运用插件化技术的动态性,能够在不发版的情况下完成热修。

说了这么多,简略的讲,插件化便是不依靠于发版使APP具备动态更新的才能。业界也管这个叫【免装置】。

2 怎样完成插件化

Android要完成插件化便是要处理三方面的问题。

  • 代码动态化。
  • 组件插件化。
  • 资源的插件化

2.1 代码(类)的动态化

正常情况下,程序员写代码 -> 打包APK -> 发版 -> 用户装置、运用。

现在要处理这样一个问题,不重新装置app的情况下,怎么让程序员编写的代码在现已被用户装置了的APP上跑起来。

Java语言的特性天然具备动态性。ClassLoader能够动态加载类文件(.class ,.dex,.jar)。Android插件化的柱石之一在于此。

编写代码然后打包成dex或许apk,然后App获取到对应的类文件,运用classLoader动态加载类,创建目标,运用反射就能够调用类/目标的方法,获取类/目标的特点。

让代码能够动态的下发动态的履行。

当然这只是一个最基本的原理,里边还有涉及到很多的细节,比方

  • 不同插件是相同classloader加载仍是不同classloader加载。
  • 宿主APP与插件APP是否是运用同一ClassLoader。
  • 假如涉及到不同ClassLoader,加载的类怎么进行通讯。

关于这些问题的处理,不同的插件化结构也有不同的计划,各有利弊,假如咱们感兴趣,后续会独自开篇详细的聊一聊。

2.2 组件插件化

上一节,说到咱们运用classloader的动态加载机制合作反射,能够让代码动态化起来。有一个很重要的问题,Android体系中Activity、Service等组件是体系组件。他的特点是体系调用体系管理的。比方Activity著名的那些回调函数,都是System_Server进程那挂了号,关于体系进程来讲是有感知的。别的一方面咱们每创建一个Activity组件都要在Manifest.xm里注册上,这个动作的含义便是让体系知道咱们的应用里有哪些组件。相应的AMS都会对注册进行校验。

假如咱们动态的下发一个Activity类,是不能像正常的类相同运行起来。怎么完成组件的插件化?

简略的说,便是占坑+转掉.

既然不能动态的在Manifest.xml清单文件里动态的注册,可是能够在Manifest里预埋几个等用的时分拿出来用,处理注册问题。

既然生命周期函数都是体系调用的,不能咱们触发,咱们能够完成转调。简略的说发动一个插件Activty的时分,其实先发动占坑的Activity -> 加载创建插件Activity(当作一个一般的类目标) -> 占坑的Activity转调插件Activity。

关于组件的插件化大约思想如此,具体完成上也不同结构也会有不同的计划,hook的点也不相同。Replugin hook了ClassLoader,使得在加载占坑activity的时分替换为了加载插件的Activity。VirtualApk hook 了Instrumentation来模仿体系发动Activity等。

当然真实完成起来仍是有一些问题需求处理,比方多进程的完成、不同发动形式的完成等。

2.3 资源的插件化

正常开发咱们运用比如 R.xx.x的方法索引资源,可是假如咱们在一个插件的Activity中假如不做处理,直接运用该方法去是索引不到资源的。由于此时是在宿主的Resource中查找插件的资源。

插件Apk中的图片,layout等资源也是需求进行插件化处理,使得能够正确的访问到插件中的资源。资源的插件化核心是对插件APK中的Resource目标实例化,这样通过Resource对像代码中或许访问到插件的资源。

完成 的方法主要有两种,

  • 一种是把插件中的资源合并到宿主中,这样运用宿主的Resource目标既能访问到插件的资源也能访问到宿主的资源。这种方法也会带来一个比较头疼的问题,资源冲突问题,通常的计划是id固定,这儿就不做打开。

  • 别的一种计划为插件创建独自的Resource目标。

packageArchiveInfo.applicationInfo.publicSourceDir = archiveFilePath
packageArchiveInfo.applicationInfo.sourceDir = archiveFilePath      
val resource = packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo)

3 其他

经过以上,能够完成一个插件化最核心的东西,除此之外,还需求做

  • 插件的装置,插件apk的解压,释放。
  • 插件的注册,使得宿主和其他插件能够发现目标插件并与之通讯。
  • 试想这样一种场景,宿主中现已依靠了某个library(A),咱们插件中也依靠A。作为插件中A的这个依靠是不是便是重复的,怎么处理这一个问题。
  • 编译器插件的生成。

4 结

从比较微观的视角聊了下,插件化处理的问题,以及完成一个插件化大约的主体思路,是很粗颗粒度的描述。每一部分独自拆出来去分析研究会有很多东西挖掘出来。也在文中埋了一些坑,往后视具体情况再做分享。

thx