AndroidView展示及事件分发wsm端流程梳理
对于init,zygote,systemserver进程启动可以自行搜索资料网上有很多讲这块
启动app进程:
当启动四大组件,对应的应用进程没有启动的时候。AMS会利用socket和Zygote进程通信并指定fork出来的进程执行某个类的main函数(对于Zygote来说是ZygoteInit的main函数,对于systemServer进程来说是SyatemServer的main函数,应用进程则是ActivityThread的main函数)。
如果是startActivity引起的,AMS会在通知zygote创建进程前建立ActivityStack,去PMS找出对应的ActivityInfo对象,同时创建一个新栈TaskRecord(这个是手动设置Flag)用于存放这个ActivityInfo。之后进程建立完成后会利用这个来启动Activity。
Zygote收到会fork出一个进程接着执行ActivityThread的main函数。
ActivityThread的main函数会创建一个ActivityThread对象接着调用这个对象的attach方法。
ps:构造方法会初始化Looper。创建出mh类(Handler)用于响应AMS的请求
这个时候一个应用进程就被创建出来了,但是这个进程并不知道自己对应的是哪个应用程序APK。
所以接下来把自己传递到AMS端(其实是ApplicationThreadProxy)等待AMS告诉自己对应的是哪个Package。
AMS端收到这个ATP之后,去PMS中查询最开始调用方传进来的包名对应的是哪个ProcessRecord(APK对应的结构。Android在重启或安装应用进程时PMS会进行扫描APK或者扫描安装的APK里面的清单文件信息保存在ProcessRecord中,并用一个数据结构对包名和ProcessRecord进行映射;还会保存一份已经启动的APK包名和对应的ProcessRecord对应关系)。
PMS端查到对应的ProcessRecord信息后,把有用信息(清单文件)存放一个数据结构中告诉这个新启动的APP(通过ATP)
新启动的APP上面说到只是一个进程提供了Android的运行环境,但是其并不知道对应的是哪个Package。上一步骤中AMS把信息告诉ATP,ATP会利用mh发送消息做处理,这个消息名字是BIND_APPLICATION。这个消息会进行以下处理(顺序不一定对):
首先会完善这个Process的一些信息比如oomadj(进程优先级),processname(进程名字)等信息。
1.创建出LoakedApk(标志着一个安装过的APK结构信息)
2.创建Context(资源等信息可以直接从LoadedApk里面拿)
3.反射创建清单文件设置的Application并调用onCreate
4.installContentP0rovider安装内容提供者
5.创建Instrumention(这个东西很重要后面启动activity就是利用它拿到AMS代理通信的,也是很多框架实现插件化Hook的点)
这个时候这个进程已经是一个合格的应用进程了(和某个package绑定)。
——————————————————————————————————————————————————————————————
Activity attach onCreate onStart
还记得上面说的startActivity时会保留一份TaskRecord和查询出来的ActivityInfo吗。这个时候就会用到了
当AMS吧这个进程启动之后,就会创建刚刚说的Activity了。AMS通知APT
在apt中收到创建Activity消息的时候会委托at中的mh发送消息处理,也就是performlaunchactivity,这个里面会反射创建Activity,并且会调用Activity的attach方法。
attach:利用policymanager反射创建policy类,policy再利用反射创建phonewindow类。phonewindow也就是window抽象类的具体实现。
总结:因此attach方法创建了window对象:phonewindow。
oncreate创建drcoreView,decoreview初始化状态栏标题栏,添加用户自定义view到decoreview中。接着调用onStart
setcontentview会调用window的setcontentview之后设置decorrview的actionbar内容。
window的方法也就是phonewindow中的方法。
1.里面进行判断decore是否创建没有创建进行new。==
2.再判断content是否为空代表之前是否设置过内容(也就是我们的视图),如果设置过内容则进行移除view
3.将我们传入的view添加到decoreview中
4.设置decoreview的actionbar内容
总结:初始化decoreview,并将传入view设置到decorevirw中。
目标Activity onResume 对方Activity onStop,onDestory
ATP之后收到handlerresume消息,进行window添加view展示,同时会添加一个idleHandler对象(执行内容后面说)
收到handlerresume消息,获取decoreview和window,调用window的addview方法,会调用到windowimpl中其中真正实现是windowglobal进行添加,删除,更新等操作。
WindowGlobal添加的时候会调用到ViewRoot的setView方法(这个方法里面会创建InputChannel用于事件分发后面将)接着会和WMS交互,WMS添加view
之后的添加删除更新等操作都是用的WindowGlobal对象,内部使用session(WMS代理)和WMS交互,WMS做处理
添加的哪个IdleHnadler对象是干什么的呢?
读者应该能猜到是调用调用方Activity的onStop和OnDestory的。
具体情况根据finishing状态来决定回调哪个方法。
其实上面所说的AMS和APP的交互AMS都会额外发送一个信息(记录超时的),可是在idle中进行AMS调用这个调用方的Activity的onStop,onDestory却没有设置~
wms端事件分发
使用共享内存(app端可以拿到数据,binder比内存慢)和pipe(实时性只输入一个字符主要是用于唤醒)进行通信。
wms–app:分发事件让app处理
app—wms:告知app自己处理情况
上面说到app进城中每个activity关联一个window,每个window关联一个viewrootimpl,viewrootimpl,这个对象就是用来接受wms端的通知进行处理事件的。
1.app端在resume中的时候会调用window的addView把decordview添加到window上。里面进行调用viewrootimpl的setview,该方法会和wms进行通信并绑定。
2.
setview中会创建inputchannel(这个对象构造方法里面没有进行任何操作具体初始化流程会在wms创建完inputchannel时进行通知在初始化),接着利用session(可以理解为wms的代理)将inputchannel传到wms端
3.systemserver中
wms接受到之后会进行初始化自己的inputchannel对象并且进行app端的inputchannel进行绑定(app端有一个,wms有一个)会启动两个线程一个用于接受事件,一个用于分发事件(防止接受事件耗时分发没有实时性)
注意:
1.读取线程会收到最底层dev/input的事件也就是EventHub的上传因为会注册。将其进行过滤看是否是App处理的事件。然后放到一个队列中
2.分发线程会进行读取队列消息,当上一个队列中的消息处理完成后(Looper不断循环是否处理完成)将消息存放到内存中(内存共享使得app端也有对应处理事件),并使用pipe通知app端处理(实时性)
4.app端
wms完成任务后(创建两个线程分别用于读取事件和分发事件,建立自己的inputchannel该对象是用于通知app消息处理的)
app端通过消息队列读取事件(注册Native层的),和wms端的inputchannel进行建立关联(共享内存和pipe)。
完成
流程:wms端的读取消息线程收到nativeinput的事件进行拦截看是否需要分发到app中。分发线程将事件发送到共享内存中,并调用inputchannel进行pipe通信到app端的inputchannel对象。
app端的inputchannel对象收到后,取出对应的viewrootimpl,vierootimpl在进行分发到decoreview中。
再解事件分发
共享内存提供事件到内存中,wms端的inputchannel使用pipe通知app端的inputchannel,接着app端读取共享内存中的事件。
wms端的inputchannel初始化是在app端activity调用resume方法里面viewroot调用setView方法进行的。
wms端收到add方法(viewroot的setview),申请共享内存,接着在两个inputchannel中保存各自的文件描述符,也就是进行了app端的初始化。
在将两个inputchannel互相指向对方后面就可以进行通信了~
app端的inputchannel会注册到nativeinputquene中,整个系统只有这一个。所以他需要管理不同applicantion的事件传递单独创建了一个connection类每个connection对应一个viewroot。
wms端是借助于inputmanager(整个系统也只有一个),inputmanager的业务是在nativeinputmanager的实例中。上个总结说没有用到looper和mq其实是错的
不同的是什么呢?:这里解释上一个总结说到wms没有用looper是因为wms是系统的而viewroot每个activity都有一个。wms如果做的话业务太庞大,但是wms用了另外一个办法,我创建一个线程利用他的looper去轮训不就行了吗(looper和mq都是私有的,这样wms不用关注looper线程去关注就行)只不过这个轮训的并不是fd。后面讲
app端注册事件:nativeinputquene其实就是像looper中addfd了一个输入系统的fd文件描述符,借用了handler的epoll机制当native的mq收到fd发来的request时封装成response进行处理(涉及到looper)。
接着执行的过程就是当监听到事件来临时,fd回发出一个request,native的mq收到处理将这个事件回调给nativeinputquene队列中。
wms端:nativeinputmanager在创建时就建立起了wms整个事件传递的逻辑。
wms端创建了两个线程一个inputreader读取事件,一个inputdispatcher分发事件。
inputreader回注册输入事件回调,当收到eventhub之后截取事件看是否需要activity处理,如何需要再将事件传递给inputdispatcher线程。
inputdispatcher这里用到了上面讲的looper机制,主要做两件事情:
1.对事件进行dispatcher前的处理。确定焦点,预处理等。接着放到一个队列中这个队列和viewrooot相关(也就是下面去轮询这个事件有没有处理完)
2.对looper进行轮训检查nativeinputquene是否处理完上一个事件处理完再进行吧队列中的事件写入到共享内存再用pipe通知。
reader提供处理的事件(为经过预处理)
dispatcher进行预处理事件存放到队列中,轮训looper的nativeputquene是否处理完上一个消息,处理完再进行吧预处理事件的队列中取出事件放入共享内存中,接着通知pipr。
app端的inputchannel注册到nativeinputquene中。
wms端的inputchannel注册到了disoatcher现场中。接受事件预处理事件,轮训上个事件是否处理完填入共享内存通知inputchannel进行pipe通信app端。
app端从共享内存读取事件,也就是viewroot也能知道。decoreview也能找知道,最后就是app的事件分发流程了。
ps
个人笔记,后续整理。有误地方辛苦指出一起交流~