不长不短的职业生涯里,有一段搞插件化的经历,当时所在的团队也是行业里比较知名的最早搞插件化的团队之一。虽然理论上是使用方,但因为业务的需要,要把大插件拆成更小颗粒度的小插件,所以会比较深度的做源码级别的定制修改。
插件化要解决的问题总的来说有三个方面
说了这么多,简单的讲,插件化就是不依赖于发版使APP具备动态更新的能力。业界也管这个叫【免安装】。
Android要实现插件化就是要解决三方面的问题。
正常情况下,程序员写代码 -> 打包APK -> 发版 -> 用户安装、使用。
现在要解决这样一个问题,不重新安装app的情况下,如何让程序员编写的代码在已经被用户安装了的APP上跑起来。
Java语言的特性天然具备动态性。ClassLoader可以动态加载类文件(.class ,.dex,.jar)。Android插件化的基石之一在于此。
编写代码然后打包成dex或者apk,然后App获取到对应的类文件,利用classLoader动态加载类,创建对象,利用反射就可以调用类/对象的方法,获取类/对象的属性。
让代码能够动态的下发动态的执行。
当然这只是一个最基本的原理,里面还有涉及到很多的细节,比如
对于这些问题的解决,不同的插件化框架也有不同的方案,各有利弊,如果大家感兴趣,后续会单独开篇详细的聊一聊。
上一节,说到我们利用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等。
当然真正实现起来还是有一些问题需要解决,比如多进程的实现、不同启动模式的实现等。
正常开发我们使用诸如 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)
经过以上,可以实现一个插件化最核心的东西,除此之外,还需要做
从比较宏观的视角聊了下,插件化解决的问题,以及实现一个插件化大概的主体思路,是很粗颗粒度的描述。每一部分单独拆出来去分析研究会有很多东西挖掘出来。也在文中埋了一些坑,今后视具体情况再做分享。
thx 😊