学习笔记:插件化Activity
四大组件之Activity
名词解释AMS:AMS Binder对象
AMN:app中获取AMS代理的对象
ATP:ApplicationThreadProxy用于AMS和APP通信 AT的代理对象
APT:ApplicationThread APP中的Binder对象。APT中会利用mh这个Handler发送消息做对应处理
AT:APT会调用AT得方法 AT中给mh发消息
mh App的Handler 用于接受处理AMS等系统服务发送的消息处理
原始流程startActivity交互过程如下App:
ContextTheme— ContextWrapper— Context— ContextImpl:
ContextImpl内部调用Instrumention.startActivity()传入启动Activity的类名 用于校验是否注册过没有注册抛异常 等信息 该方法其中通过AMN获取AMS代理对象和AMS进行通信。
AMS远端
获取要启动的activity类名 观察是否在清单文件中注册过 PMS在安装时会解析对应APK清单文件存储到本地 。接着给启动activity的进程发送一个pause的消息通过ATP
然后判断该进程是否启动过 没有启动需要先和zygote用socket通信先创建一个进程Process.start
1.没有启动过 没有启动需要先和zygote用socket通信先创建一个进程Process.start 接着创建ActivityThread 利用AMN把ATP传给AMS 这样AMS就可以和APP通信了
接着AMS在给ATP发消息 AT收到给mh发送消进行反射创建Application调用onCreate 在接着就是下面2中所说的进程已经启动情况的步骤了
2.启动过 AMS会封装Activity变成一个ActivityClientRecord 该对象会携带cl后面mh会根据这个cl去反射创建Activity。
APP中ATP中收到AMS发过来的消息取出ActivityClientRecord 调用AT吧这个参数传进去。
AT中调用mh的handlerPerformLaunchActivity。mh其实是一个handler handleCallBack中来决定调用哪个方法对应于本示例startActivity为下面的逻辑
回调中取出acRecord 利用acRecord的classloader对象来反射创建对应activity 并调用onCreate方法
通知最开始pause的那个activity恢复运行~ 利用ATP也是
至此交互结束
Hook点我们可以Hook哪些点呢 提炼出精华我们在哪里可以把狸猫换成太子~
第一个点Instrumention利用AMN获取到AMS代理 将信息传给AMS校验
我们在Instrumention中拦截这个代理 替换为我们自定义的对象。检测到startActivity时将Activity换成我们宿主中已经注册了的StubActivity
好了 现在AMS校验过了 他要通知APP创建Activity对象了。但是这个时候是StubService的Activity。我们需要把他换掉我们真实启动的Activity。
第二个点我们怎么知道真实启动的Activity是谁呢 在第一步中其实整个流程传递的都是Intent 包括我们接受到ANS消息时也是Intent。
既然是同一个Intent对象。我们在这个Intent中动手脚 APP调用AMS把真实启动的Activity保存到Intent中 在AMS通知APP创建的时候在取出来
好 第一个是在Instrumention中换成我们的代理接下来就会和AMS交互 这里是最后一步下一步就要和AMS通信因此选在这里进行Hook
那么通知ATP,AT,mh中都可以进行还原取出Intent中真实启动的activity。
我们在mh中最后一步反射创建的地方进行修改 和上面一样 在最接近现场的地方进行Hook 。将mh替换为我们的mh。这样我们收到handlerPerformLaunchActivity的消息时 就可以做我们自己的操作了
不同的ClassLoader会加载不同的类 classloader中如果没有这个class是加载不了的。所以我们要用正确的cl去加载对象。这个cl其实就是插件的cl 也就是dexcl。我们在和AMS交互的时候可以传入对应的cl 之后创建再取出正确的cl。
注意点由于Activity默认是Standard模式 所以宿主中的一个Activity可以对应插件中的多个标准模式的Activity。每次启动都会创建一个实例。
但是Android中是有LaunchMode的 不同的启动模式对应的效果也不同。那么如何支持LaunchMode呢
LaunchMode的支持其实就是Activity栈的变化。我们自己手动保存一份当前启动的Activity信息集合 根据启动Activity的launchmode去操作这个集合并对集合中的Activity实例做处理【这个也是Android原生的处理方式】
要保存两个集合一个是已经启动的集合commentName为key 真实的Activity实例为value 这个模拟的就是Activity栈 一个是当前APK中所有的Activity集合 是否可以通过类名找到对应的Activity信息 name对应ActivityComment
注 第二个集合需要在Application的attachBaseContext中去完善这个集合。越早越好
流程
1.先遍历第二个集合中看插件中是否有对应Activity的信息。很好理解如果你启动一个压根就不存在的Activity之后反射也会崩溃~~
这之后的代码逻辑就是mh中Hook那个handlerPerformLaunchActivity的消息时的处理 针对创建Activity实例和调用Activity方法进行额外的逻辑处理
2.首先看下已经启动的集合中Activity的数量 launchmode是针对多Activity才有用 如果栈中只有一个Activity那么launchmode将没用
3.再接着取出这个activity的launchmode是不是非Standard的 默认模式不需要处理 直接把他加到我们的第一个已经启动activity的集合中即可。原生也是这样
3.1SingleTop
首先看下原生的这个启动模式是什么样的情况
如果栈顶是这个Activity那么将不会创建实例也不会调用onCreate 而是会调用他的onNewIntent方法。否则就是按Standard处理
我们怎么处理呢~~
注意 本质上我们还是standard的
原先 S1– T1 之后就是S1– T1 有状态的 — T1了
Question 我们可以把之前Top的T1干掉吗也就是中间的那个T1 就是S1— T1了~
A 不行 这样启动的T1状态会丢失。真实的情况应该仅仅是调用T1(有状态的)的onNewInstance
怎么做呢 不添加这个后来的activity 直接取出栈顶的activity实例调用OnNewInstance即可~~
对应逻辑
先看下栈顶是不是这个activity。
~如果不是就直接加入到集合中就行 也就是默认Standard模式
~如果是的话 则忽略这个要启动的Activity 把栈顶的Activity取出来调用onNewInstance即可
3.2SingleTask
首先看下原生的这个启动模式是什么样的情况
如果在栈中已经有该Activity实例了 那么清除掉在它上面的所有Activity 并调用这个Activity的onNewInstance方法
根据前面那个mode的处理 应该也能猜出怎么做了
在集合中找出这个Activity实例和他对应的下标。进行对这个范围 下标–集合最大数量 的activity实例进行挨个销毁。
接着在调用这个activity的onNewInstance方法
3.3 SingleInstance一样的操作。
相关文章
- 深度学习笔记------现阶段的目标检测器结构解析(Neck[FPN,PANet,Bi-FPN],Head[rpn,yolo...])
- DETR(Detection with Transformers) 学习笔记
- 清华大学C++课程学习笔记——第五章 数据共享与共享数据的保护
- Vue学习笔记之scope.row的最简单理解
- Vue学习笔记之vue-cli脚手架安装和webpack-simple模板项目生成
- Vue学习笔记之Nodejs入门
- kettle学习笔记(三)——kettle资源库、运行方式与日志
- HDFS学习笔记二
- python学习笔记 程序执行过程 基本数据类型
- 第7章 PCA与梯度上升法 学习笔记中
- Caliburn.Micro学习笔记(五)----协同IResult
- 【笔记】Bootstrap入门视频-吴华-CSDN
- ffmpeg学习笔记
- Java泛型的学习笔记[1]—基础知识
- JavaScript笔记Array.filter(Boolean)
- 《计算机思维》笔记
- SpringCloud学习笔记:熔断器Hystrix(5)
- 吴恩达深度学习网课 通俗版笔记——(02.改善深层神经网络)第二周 优化算法
- A40i使用笔记:远程ftp复制程序不执行(权限问题)
- cmake 学习笔记(三)