目录:
搬砖码字不易,转载请注明转自:http://blog.csdn.net/u011176685/article/details/52006502
上一篇主要介绍插件化的一些概念和作用,以及我为什么选择Small。现在来具体介绍下small。
一、Small的原理
1.动态加载class
![](https://i-blog.csdnimg.cn/blog_migrate/eccb9e0e158be8ca4df2b7a313c9104c.webp?x-image-process=image/format,png)
Android类由DexClassLoader 加载,如果直接在编译搜索这个类的时候出现下面这种情况,我开始以为是没有关联源码,还折腾了好一会,后面发现,应该是特意搜不到了。不过开源直接在https://android.googlesource.com 搜。请自备梯子,我一般是用lantern+谷歌浏览器。
DexClassLoader的构造方法里面调用了父类的构造方法,我们跟进去可以看到BaseDexClassLoader 里面有个dexPath参数,这个通常是应用储存apk的目录/data/../*.apk。现在用来作为创建pathList。我们再看看new DexPathList里面做了什么。
![](https://i-blog.csdnimg.cn/blog_migrate/f49d07871785dd33b8e934eb8879e6e0.webp?x-image-process=image/format,png)
DexPathList里面调用makeDexElements方法创建dexElements。我们跟进去看看做了什么。
![](https://i-blog.csdnimg.cn/blog_migrate/4a26029a97e896bec643e6d44dac5a85.webp?x-image-process=image/format,png)
可以看到,DexClassLoader不支持".so"后缀。那么small是怎么做的呢,看看伪代码。
![](https://i-blog.csdnimg.cn/blog_migrate/72b8d5b4478f0c0f8ae0b75495c4e730.webp?x-image-process=image/format,png)
为了让应用启动时能自动复制插件包到应用存储目录,需要支持".so"后缀。做法就是模拟压缩包加载代码块,创建一个dex元素,再反射添加到宿主class loader里的dexPathList。后面的演示过程中,你会看到small更新的所谓插件,就是编译过后的so文件。
2.动态加载resources
![](https://i-blog.csdnimg.cn/blog_migrate/2f9e38054c521254864d189d0c75dfaa.webp?x-image-process=image/format,png)
安卓资源由AssetManager加载。应用启动时,系统会为其创建一个AssetManager实例,并由addAssetPath方法添加资源搜索路径,默认添加:"/framework/base.apk" - Android base resources (base),"/data/app/*.apk" - The launching apk resources (host)
![](https://i-blog.csdnimg.cn/blog_migrate/6ffdec81b6067d0742c51949fc0f5c1e.webp?x-image-process=image/format,png)
![](https://i-blog.csdnimg.cn/blog_migrate/888c160ccd0baf20a109c56526091118.webp?x-image-process=image/format,png)
所有的资源需要通过一个唯一的id来访问,通常是形如0xPPTTNNNN,每个字段PPT的那个图有相应的介绍,我就不多说了。那么如何处理这个资源id分配的问题呢?有下面几种方案
![](https://i-blog.csdnimg.cn/blog_migrate/4c105e84c71524a8dab3b501525313c6.webp?x-image-process=image/format,png)
但是这几种方案都有问题,为什么有问题的方案还会提?我是觉得这样可以养成一种思考的能力。第一种会导致插件之间不能访问资源。2.1字段有限,不好维护;2.2插件间无法访问资源;2.3修改aapt源码,不好维护。如果修改aapt生成产物,无缝连接,支持极致剪裁。资源包进行重新打包,重设资源id,small就是采取这种方式。具体深入的实现,这里就不提了。感兴趣的可以继续查看源码看看。
3.动态注册activities
![](https://i-blog.csdnimg.cn/blog_migrate/c0dba9bc3b9d84687390b63bbdafa6c0.webp?x-image-process=image/format,png)
每一个activity由Activity的startActivityForResult方法启动,通过instrumentation的execStartActivity方法激活生命周期。这个地方请大家留意下,后面也会提到这里。
![](https://i-blog.csdnimg.cn/blog_migrate/f82ae9110aaf15d53919768191c1e623.webp?x-image-process=image/format,png)
在ActivityThread的performLaunchActivity方法中通过instrumentation的newActivity方法实例化。
如果要动态注册Activity,首先在宿主manifest中注册一个命名特殊的占坑activity来欺骗mInstrumentation.execStartActivity以获得生命周期,再欺骗mInstrumentation.newActivity来获得插件activity实例。Small封装一个instrumentation,来替换掉宿主。可以看到small里面的占坑。
写了这么多,,我都有点累了,相信此时看的你也一定有点累,但是知识岂有不付出就能得到的?加油吧!!大概就是这些,后面将介绍Small的使用,加载,启动,更新。实战开始!!APP项目如何与插件化无缝结合(三)
欢迎关注个人微信公众号,专注于Android深度文章和移动前沿技术分享
参考资料:
1.https://github.com/wequick/Small
2.http://www.tuicool.com/articles/NB32EjY
3.http://www.tuicool.com/articles/RR3QrmV