你们好,我系苍王。
架构
如下是我这个系列的相关文章,有兴趣能够参考一下,能够给个喜欢或者关注个人文章。app
[Android]如何作一个崩溃率少于千分之三噶应用app--章节列表ide
什么是占坑?为何要占坑?函数
Android插件化中,从一个插件Activity跳转到不一样插件的Activity的时候,是否能够能正常跳转成功?组件化
声明Activity须要配置什么?学习
声明Activity是须要AndroidManifest中声明,可是插件是依赖于宿主的,插件声明了Activity,可是插件的AndroidManifest信息,是没法动态配置到宿主里面的。gradle
那AndroidManifest在何时注入的?其实这个是在App安装的时候,PackageManagerService就会读取到AndroidManifest里面的配置信息并保存一份到PackageManagerService.Settings当中,那么基本没法动态的改变这份配置信息。若是之后能动态的改变Android中记录的App配置信息,那么咱们就不须要占坑了。ui
正由于一开始就已经将配置AndroidManifest记录到PackageManagerService,里面的记录的Activity的信息,将也会保存到PackageManagerService中。咱们使用startActivity的时候,ActivityManagerService将会Activity的合法信息传送到Native层做配置验证,若是没法找到跳转Activity的配置,那么将抛出异常。插件
插件是app运行时,动态将插件信息插入classLoader的dex列表当中。可是宿主的AndroidManifest配置是没法动态去配置修改的。那么插件中的跳转,如何越过这种困境呢?3d
工程师聪明的,他们提早在宿主声明一些空Activity信息到AndoridManifest当中,而后在使用startActivity后在ActivityManagerService中在跳转到Native层前将替换成员AndroidManifest的空Activity,欺骗验证,而后Native层验证事后,在传回ActivityManagerService层后替换回须要跳转的Activity的信息。这种声明空Activity信息到AndroidManifest的行为,咱们就叫作占坑了。
结合上一节,hook点来看,占坑替换是须要hook掉ActivityManagerService来完成这样的操做的,可是上一节已经介绍过Replugin惟一hook点在classloader了,那么这个占坑替换又是如何完成呢?
宿主在引入gradle-host-library的时候,就已经引入了Replugin的占坑操做了。
Replugin在库中的AndroidManifest,已经提早的声明了各类各样的Activity Service Proivder,而后BroadcastReceiver能够动态注册,因此并不须要占坑。
咱们能够看到${applicationId}它将会直接引用到宿主app build.gradle中的applicationId完成。
咱们能够看到这些坑位会被合并到在宿主的full的AndroidManifest.xml里面。
你拖到最后,会发现除了这些坑位外,还会有不少360的坑位添加了,这是如何作到的呢?
这里关键在于引用了replugin-host-gradle中的配置,咱们在ComponentsGenerator.groovy文件,会使用Gradle命令编译时生成这些占坑声明。以后深刻介绍replugin的gradle文件的时候,会给你们更加深刻介绍。
咱们看一下使用Replugin封装的的跳转
很清晰的看到pluginName,至关于Android的包名来填写。
startActivity的时候,从intent中获取包名和类名,而后再调用Factory.startActivityWithNoInjectCN
而后继续使用插件管理的继续跳转函数
这里IPluginManager对参数等说明很是状况,说明是公司的技术追求仍是很高的。
进入到底层的PmLocalImpl中
而后更深刻添加参数
在PmInternalImpl终于能够看真正实现,这里先要判断是否插件已经下载,getPluginConfigInfo会获取是否存在手机中是否存在插件。
而后isNeedToDownLoad来启动下载。
这里就是一些基础的下载封装了。
下载时须要上锁处理,表示当前插件正在生效。ProcessLocker是自定义的进程锁。
PluginProcressMain.getPluginHost().pluginDownloaded将会下载并加载插件,咱们留到下一节再介绍,这个过程。
tryLock和unlock的调用就是对进程的锁定了。
ProcessLock里面,是使用文件锁来完成上锁的,这里的进程锁,正确的来讲是文件锁。
这里面建立出文件后缀为.lock的文件,做为文件锁,而后建立出FileOutputStream为输出通道,
Java NIO中的FileChannel是一个链接到文件的通道。能够经过文件通道读写文件。
FileChannel没法设置为非阻塞模式,它老是运行在阻塞模式下。
这里的FileChannel是FileOutputStream中获取的通道的。
这里面须要释放的时候,须要释放Filelock,FileChannel, FileOutStream, File,四个对象造成的锁。
当正常安装之后,了经过获取到PluginInInfo来判断插件是否成功安装
而后再次下载中会经过onPluginNotExitsForActivtiy,来回调提示。
若是activity是动态注册的类,直接使用startActivity打开
这里面须要判断插件中有注册到注册的Activity类
这里是经过HashMap来保存类的列表
这里其余插件首先会在Entry的入口里面在init的时候调用注册的方法注册,建立出一个ProxyRePluginVar的远程插件信息。
其会建立出两个startActivity的MethodInvoker反射的类,来用于使用跳转方法。其会分发到不一样的插件的RePlugin的对象
Broadcast,Provider,Service,四大组件都是经过这种反射调用的方式,来提供其余插件调用的。
回到PmInternalImpl,插件损坏或者其余缘由状态异常,判读会返回跳转目标不存在
若是是大插件,会使用onLoadLargePluginForActivity的方法启动。
这里真正的启动占坑的方式来作跳转
咱们看到loadPluginActivity当中,经过ActivityInfo 来保存一个Activity的信息,而后
这里判断进程和远程分配坑位。
若是有分配,马上进入监控状态,并强制使用UI进程运行。
使用bindActivity来绑定Activity.
bindActivity当中,继续调用到PluginContainter的alloc分配
最终会调用到allocLocked分配,里面有四种规则
(1)尝试找找到一个动态注册过的。
(2)找一个新分配的
(3)重用,最老的一个
(4)挤掉最老的一个
而后经过坑位跳转
咱们在plugin-lib中的插件须要依赖的库中找到
其PluginActivity是替换的Activity
可是实际上demo中并无使用继承PluginActivity的例子。都是使用占坑逻辑来替换,并不必定要使用PluginActivity。使用PluginActivity是嵌套生命周期的方法给Repluin管理。
Replugin占坑跳转的判断是我研究插件化以来最复杂的,代码量也很大。
我创建了一个关于Android架构学习的群,里面能够进一步进行组件化学习的交流。
群号是316556016,也能够扫码进群。我在这里期待大家的加入!!!