硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)相关的知识,希望对你有一定的参考价值。
前言
短短几个月,看着越来越多的读者通过我写的文章而关注公众号,我好开心,同时也诚惶诚恐,害怕无法持续输出高质量文章,让读者失望。但我知道,只有写出让读者心动的文章,才能得其心,会好好加油,为大家持续输出精品文章的。
本文分享Framework相关内容,提纲如下:
💡一丶Framework通信
1.1.android Binder
进程隔离:
内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据 为了保证系统的安全,用户空间和内核空间是天然隔离的 每个进程有自己的虚拟内存空间,为了安全,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间
为什么要用Binder
?
-
Android
系统内核是Linux
内核 -
Linux
内核进程通信有:管道、内存共享、Socket
、File
; -
对比:
Binder
的一次拷贝发生在用户空间拷贝到内核空间;
用户空间: App
进程运行的内存空间;
内核空间: 系统驱动、和硬件相关的代码运行的内存空间,也就是进程ID为0的进程运行的空间;
程序局部性原则: 只加载少量代码;应用没有运行的代码放在磁盘中,运行时高速缓冲区进行加载要运行的代码;默认一次加载一个页(4K),若不够4K就用0补齐;
MMU:内存管理单元;
给CPU提供虚拟地址;
当对变量操作赋值时:
-
CPU拿着虚拟地址和值给到MMU
-
MMU用虚拟地址匹配到物理地址,MMU去物理内存中进行赋值;
物理地址: 物理内存的实际地址,并不是磁盘;
虚拟地址: MMU根据物理内存的实际地址翻译出的虚拟地址;提供给CPU使用;
页命中:CPU读取变量时,MMU在物理内存的页表中找到了这个地址;
页未命中:CPU读取变量时,MMU在物理内存的页表中没有找到了这个地址,此时会触发MMU去磁盘读取变量并存到物理内存中;
普通的二次拷贝:
应用A拷贝到服务端:coay_from_user
从服务端拷贝到应用B:coay_to_user
mmap():
-
在物理内存中开辟一段固定大小的内存空间
-
将磁盘文件与物理内存进行映射(理解为绑定)
-
MMU将物理内存地址转换为虚拟地址给到CPU(虚拟地址映射物理内存)
共享内存进程通信:
-
进程A调用
mmap()
函数会在内核空间中虚拟地址和一块同样大小的物理内存,将两者进行映射 -
得到一个虚拟地址
-
进程B调用
mmap()
函数,传参和步骤1一样的话,就会得到一个和步骤2相同的虚拟地址 -
进程A和进程B都可以用同一虚拟地址对同一块映射内存进行操作
-
进程A和进程B就实现了通信
-
没有发生拷贝,共享一块内存,不安全
Binder
通信原理:
角色:Server
端A、Client
端B、Binder
驱动、内核空间、物理内存
-
Binder
驱动在物理内存中开辟一块固定大小(1M-8K)的物理内存w,与内核空间的虚拟地址x进行映射得到 -
A的用户空间的虚拟地址ax和物理内存w进行映射
-
此时内核空间虚拟地址x和物理内存w已经进行了映射,物理内存w和
Server
端A的用户空间虚拟地址ax进行了映射:也就是 内核空间的虚拟地址x = 物理内存w =Server
端A的用户空间虚拟地址ax -
B发送请求:将数据按照binder协议进行打包给到
Binder
驱动,Binder
驱动调用coay_from_user()
将数据拷贝到内核空间的虚拟地址x -
因步骤3中的三块区域进行了映射
-
Server
端A就得到了Client
端B发送的数据 -
通过内存映射关系,只发生了一次拷贝
Activity
跳转时,最多携带1M-8k(1兆减去8K)的数据量;
真实数据大小为:1M内存-两页的请求头数据=1M-8K;
应用A直接将数据拷贝到应用B的物理内存空间中,数据量不能超过1M-8K;拷贝次数少了一次,少了从服务端拷贝到用户;
IPC通信机制:
-
服务注册
-
服务发现
-
服务调用
以下为简单的主进程和子进程通信:
1、服务注册: 缓存中心中有三张表(暂时理解为三个HashMap
,Binder
用的是native
的红黑树):
-
第一种:放
key
:String
-value
:类的Class
; -
第二种:放
key
:Class
的类名 -value
:类的方法集合; -
第三种:放key :
Class
的类名 -value
:类的对象;
类的方法集合:key
-value
;
key
:方法签名:“方法名” 有参数时用 “方法名-参数类型-参数类型-参数类型…”;
value
: 方法本身;
注册后,服务若没被调用则一直处于沉默状态,不会占用内存,这种情况只是指用户进程里自己创建的服务,不适用于AMS
这种;
2、服务发现: 当被查询到时,要被初始化;
-
客户端B通过发送信息到服务端A
-
服务端解析消息,反序列化
-
通过反射得到消息里的类名,方法,从注册时的第一种、第二种表里找到
Class
,若对象没初始化则初始化对象,并将对象添加到第三种的表里;
3、服务调用:
-
使用了动态代理
-
客户端在服务发现时,拿到对象(其实是代理)
-
客户端调用对象方法
-
代理发送序列化数据到服务端A
-
服务端A解析消息,反序列化,得到方法进行处理,得到序列化数据结果
-
将序列化结果写入到客户端进程的容器中;
-
回调给客户端
AIDL
: BpBinder
:数据发送角色 BbBinder
:数据接收角色
编译器生成的AIDL
的java
接口.Stub.proxy.transact()
为数据发送处;
发送的数据包含:数据+方法code+方法参数等等;
-
发送时调用了
Linux
的驱动 -
调用
copy_from_user()
拷贝用户发送的数据到内核空间 -
拷贝成功后又进行了一次请求头的拷贝:
copy_from_user()
-
也就是把一次的数据分为两次拷贝
请求头:包含了目的进程、大小等等参数,这些参数占了8K
编译器生成的AIDL
的java
接口.Stub.onTransact()
为数据接收处;
Binder
中的IPC
机制:
-
每个App进程启动时会在内核空间中映射一块1M-8K的内存
-
服务端A的服务注册到
ServiceManager
中:服务注册 -
客户端B想要调用服务端A的服务,就去请求
ServiceManager
-
ServiceManager
去让服务端A实例化服务:服务发现 -
返回一个用来发送数据的对象
BpBinder
给到客户端B -
客户端B通过
BpBinder
发送数据到服务端A的内核的映射区域(传参时客户端会传一个reply
序列化对象,在底层会将这个地址一层一层往下传,直至传到回调客户端):这里发生了一次通信copy_from_user
:服务调用 -
服务端A通过
BBBinder
得到数据并处理数据 -
服务端唤醒客户端等待的线程;将返回结果写入到客户端发送请求时传的一个
reply
容器地址中,调用onTransact
返回; -
客户端在
onTransac
中得到数据;通信结束;
ServiceManager
维持了Binder
这套通信框架;
1.2.Android Handler
Handler、Message、MessageQueue、Looper;
以下为零散的记录,最后有总结; 内存泄露的本质:
长生命周期对象持有短生命周期对象,导致短生命周期对象销毁不掉;
持有链:
线程>>Looper
>>MessageQueue
>>Message
>>Handler
>>Activity
;
Message对象的变量target为发送消息的Handler; MessageQueue
队列里放Message
; Looper
对象里实例化MessageQueue
; 一个线程绑定一个Looper
;
为什么要有handler
? 主要目的是要解决线程切换问题,handler
里的Message
机制解决了线程间通信;
为什么有队列MessageQueue
? MessageQueue
是一个单向链表,next()
调用nativePollOnce->lunx
的epoll_wait()
等待实现阻塞时队列;
-
在单线程中一次只能执行一句代码
-
假如发送了一个大消息A
-
处理这个大的消息A
-
但是处理的太慢了
-
从而导致其他后续要发送的消息发不出去
-
因为单线程阻塞到了第3步处理那个消息A的地方
队列的出现解决了"处理消息"阻塞到"发送消息"的问题;
队列是生产者消费者模式;
而要使用队列需要至少两个线程、和一个死循环;
-
一个线程负责生产消息;
-
一个线程消费消息;
-
死循环需要取出放入队列里的消息;
为什么有Looper
?
为了循环取出队列里的消息;
一个线程有几个Looper
,为什么不会有多个?
一个线程一个Looper
,放在ThreadLocalMap
中;
假如Looper
对象由Handler
创建,每创建一个Handler
就有一个Looper
,那么调用Looper.loop()
时开启死循环;在外边调用Looper
的地方就会阻塞;
主线程中Looper
的死循环为什么没有导致系统卡死?
-
我们的UI线程主线程其实是
ActivityThread
线程,而一个线程只会有一个Looper
; -
ActivityThread.java
的main
函数是一个APP进程的入口,如果不卡死,main
函数执行完则整个应用进程就会退出; -
android
是以事件为驱动的操作系统,当有事件来时,就去做对应的处理,没有时就显示静态界面;
获取当前线程:Thread.currentThread();
ThreadLocalMap
:类似于HashMap;
每个Thread
对象都有一个对应的ThreadLocalMap;
在Looper.prepare()
时,存入Looper
,存Looper
时ThreadLocalMap
的key为ThreadLocal
,value
为Looper
;
内存抖动根本的解决方式是复用;
handler.obtainMessage();
-
从
Looper
的回收池中取Message
; -
Message
是一个单向链表,Message
不是一个单纯的对象,而是一个链表集合 -
最大长度固定50个
Linux
函数: epoll_create
:App
注册进红黑树中,拿到一个事件fd的值; epoll_ctl
:注册事件类型,监听fd是否改变(Linux
中事件都会被写入文件中,如触摸屏幕事件会写入到:dev/input/event0
文件中),fd有改变时唤醒epoll_wait
; epoll_wait
:有事件时就分发,没事件就阻塞
总结: handler
如何做的线程切换的? 首先Handler
的使用步骤:
-
调用
Looper.prepare();
-
创建
Handler
对象; -
调用
Looper.Loop()
方法。 -
线程中发送消息。
在第一步时,创建一个Looper
,并放到当前线程的变量threadLocals
中;threadLocals
是一个map,key为ThreadLocal对象本身,value为Looper;在Looper.loop()
时取出;
第二步,用户在当前线程(可能是子线程)创建Handler
对象;
第三步,Looper.loop()
一直在死循环,Looper.loop()
这句代码下面的代码是不会被调用的,调用Looper.loop()
函数时,先从当前线程的map变量中取出Looper
,再从Looper
中拿到队列MessageQueue
,for循环中不断从队列中取出消息;
第四步,在其他线程调用handelr
发送消息时,Message
里有个target
,就是发送消息的handler
;
在Looper.loop()
时,队列中取到消息时,调用msg.target.dispatchMessage(msg);
其实就是handler
对象.dispatchMessage(msg);
所以不论在哪个线程调用发送消息,都会调用到handler
自己分发消息;而handler
所处的线程是创建时的“当前线程”,所以处理时也就回到了“当前线程”;实现了线程切换,和线程通信;
Looper
的死循环为什么不会让主线程卡死(或ANR
)? 简单版:
-
我们的UI线程主线程其实是
ActivityThread
所在的线程,而一个线程只会有一个Looper
; -
ActivityThread.java
的main
函数是一个APP进程的入口,如果不一直循环,则在main
函数执行完最后一行代码后整个应用进程就会退出; -
android
是以事件为驱动的操作系统,当有事件来时,就去做对应的处理,没有时就显示静态界面; -
ANR
发生条件是:Activity
:5 秒。应用在 5 秒内未响应用户的输入事件(如按键或者触摸)BroadCastReceiver
:10 秒。BroadcastReceiver
未在 10 秒内完成相关的处理Service
:20 秒(均为前台)。Service
在20 秒内无法处理完成 -
如果
Handler
收到以上三个相应事件在规定时间内完成了,则移除消息,不会ANR
;若没完成则会超时处理,弹出ANR
对话框;
详细:
-
App
进程的入口为ActivityThread.java的main()
函数,注意ActivityThread
不是一个线程; -
应用的ui主线程实际是调用
ActivityThread.java的main()
函数执行时所在的线程,而这个线程对我们不可见,但是这就是主线程;参考: -
在
ActivityThread.java
的main()
函数中,会调用Looper.prepareMainLooper();
-
Looper.prepareMainLooper()
会创建一个Looper并放到当前线程(主线程)的变量threadLocals
中进行绑定,threadLocals
是一个ThreadLocal.ThreadLocalMap;
-
在
ActivityThread.java
的main()
函数结尾,开启Looper.loop()
进行死循环,不让main函数结束,从而让App进程不会结束; -
Android系统是以事件作为驱动的操作系统,当有事件来时,就去做对应处理,没有事件时,就显示当前界面,不做其他多余操作(浪费资源);
-
在
Looper.loop()
的死循环中,不仅要取用户发的事件,还要取系统内核发的事件(如屏幕亮度改变等等); -
在调用
Looper.loop()
时,从MessageQueue.next()
中获取事件,若没有则阻塞,有则分发; -
MessageQueue
其实不是一个队列,用epoll机制实现了阻塞; -
在
Looper.prepareMainLooper()
时,调用c++函数epoll_create()
会将App注册进epoll机制的红黑树中得到fd的值,epoll_ctl()给每个App注册事件类型并监听fd值是否改变,fd有改变时唤醒epoll_wait; -
epoll_wait()
有事件时就分发,没事件就阻塞
子线程的Looper和子线程Looper有什么不同?
子线程Looper是可以退出的,主线程不行;
1.3.Jetpack之LiveData订阅与数据分发
使用:
public class LiveDataActivity extends AppCompatActivity
MutableLiveData liveData= new MutableLiveData<String>();
void liveDataTest()
// 任何线程都可以发送数据
liveData.postValue("postValue");
// 只有主线程可以发送数据
// liveData.setValue("setValue")
void observeTest()
//订阅
liveData.observe(this, new Observer<String>()
@Override
public void onChanged(String data)
//收到数据data
);
//可以有多个订阅
liveData.observe(this, new Observer<String>()
@Override
public void onChanged(String data)
//收到数据data
);
阶段一:
postValue
:
protected void postValue(T value)
boolean postTask;
synchronized (mDataLock)
postTask = mPendingData == NOT_SET;
mPendingData = value;
if (!postTask)
return;
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- 将用户发送的数据给到一个
mPendingData
的变量; - 切换到主线程
- 执行了一个
mPostValueRunnable;
mPostValueRunnable
:
volatile Object mPendingData = NOT_SET;
private int mVersion;
private final Runnable mPostValueRunnable = new Runnable()
@SuppressWarnings("unchecked")
@Override
public void run()
Object newValue;
synchronized (mDataLock)
newValue = mPendingData;
mPendingData = NOT_SET;
setValue((T) newValue);
;
- 在
mPostValueRunnable
中将mPendingData
给到了新的临时变量newValue; mPendingData
的值置为空;- 调用
setValue(newValue);
setValue
:
private volatile Object mData;
private int mVersion;
@MainThread
protected void setValue(T value)
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
- 将数据版本mVersion +1;
- 将发送的数据给到了
mData
; - 调用分发数据
dispatchingValue
看到这里发现,数据其实最后存到了mData
中;若想发送订阅消息,肯定得添加订阅者;
阶段二:
添加订阅者,observe()
:
//用户使用:
//liveData.observe(this@LiveDataActivity,
//
//)
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
assertMainThread("observe");
//如果被观察者的生命周期是DESTROYED,就不添加订阅者
if (owner.getLifecycle().getCurrentState() == DESTROYED)
// ignore
return;
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
...
owner.getLifecycle().addObserver(wrapper);
- 将有生命周期的
Activity
和订阅者Observer
传了进来; - 判断
Activity
的生命周期; - 将
Activity
和Observer
封装为一个LifecycleBoundObserver
对象; - 将
LifecycleBoundObserver
放到了mObservers
这个Map集合中; map
的key
为观察者,value
为封装了activity
和观察者Observer
的对象LifecycleBoundObserver;
LifecycleBoundObserver
及它的父类ObserverWrapper
:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer)
super(observer);
mOwner = owner;
...
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event)
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED)
removeObserver(mObserver);
return;
Lifecycle.State prevState = null;
while (prevState != currentState)
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
...
@Override
void detachObserver()
mOwner.getLifecycle().removeObserver(this);
//ObserverWrapper
private abstract class ObserverWrapper
//传进来的观察者放这里了
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer)
mObserver = observer;
...
void activeStateChanged(boolean newActive)
if (newActive == mActive)
return;
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive)
dispatchingValue(this);
- 将有生命周期的
Activity
给到了LifecycleBoundObserver的mOwner
; - 将观察者给到了
LifecycleBoundObserver
的父类ObserverWrapper
的mObserver
; - 其实
LifecycleBoundObserver
就可以获取到Activity
和mObserver
; - 上一步,看到将封装后的
LifecycleBoundObserver
放到了mObservers
这个map
中; map
的key
为观察者,value
为封装了activity
和观察者Observer
的对象LifecycleBoundObserver
;
阶段三:
分发:
在阶段一setValue()
时,调用了dispatchingValue(null);
void dispatchingValue(@Nullable ObserverWrapper initiator)
if (mDispatchingValue)
mDispatchInvalidated = true;
return;
mDispatchingValue = true;
do
mDispatchInvalidated = false;
if (initiator != null)
considerNotify(initiator);
initiator = null;
else
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); )
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated)
break;
while (mDispatchInvalidated);
mDispatchingValue = false;
private void considerNotify(ObserverWrapper observer)
if (!observer.mActive)
return;
...
if (observer.mLastVersion >= mVersion)
return;
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
- 当为
null
时,走到了for循环 - 循环取出
map
中的value
,而value
就是LifecycleBoundObserver;
LifecycleBoundObserver
中有mObserver
,mObserver
就是用户传的观察者;mObserver.onChanged((T) mData);
- 完成了“主动”分发;
💡二丶Framework
底层服务
2.1.Android Framework
源码-AMS
APP的启动到绘制过程中,AMS源码核心应用方案
AMS(ActivityManagerService) 在SystemServer
的进程中,是SystemServer
中的一个对象;
作用:
-
管理
activity
的生命周期 -
启动
activity
-
与
PMS
进行交互Activity
->AMS
: -
调用
activity.startActivity()
-
通过
ActivityManage.getService("activity")
得到AMS
的BpBinder
; -
通过
BpBinder
发送请求,调用AMS的startActivity()
AMS
->PMS
:
-
AMS
和PMS
都在SystemServer
进程中,都是SystemServer
中一个对象 -
通过包名和
PMS
里的缓存mPackage
查询到App
对应的Package
-
使用
activity
的类名通过PMS
里的内部类PackageManagerInternalImpl
查询到activity
对应的包装类ResolveInfo; ps:ResolveInfo
这个javabean
里有activityInfo、ServiceInfo
等变量,查询啥就给哪个变量赋值,再返回ResolveInfo;
-
得到
ResolveInfo
里的activityInfo;
-
将
activityInfo
返回给App进程的ActivityThread;` -
ActivityThread
中发送事件 -
ActivityThread
中的Handler
对象mH收到159事件,处理 -
通过反射创建
Activity
对象 -
将
Activity
对象放到activtes
启动记录中
ActivityThread
-
每个应用有一个
ActivityThread
;是应用的入口; -
在
APP
进程中 -
是
AMS
的缓存中心 -
ActivityThread
中的List<ActivityRecord> activtes
放了activity
的启动记录
ActivityThread
中重要的对象:
-
ApplicationThread
:AMS
回调给ActivityThread
数据的桥梁 -
mInstrumentation
:管理Application
和Activity
的生命周期(及创建) -
mH:
Handler
,处理ApplicationThread
里各种回调函数发送的各种消息
点击桌面App
图标发生了什么?
-
点击的
APP
图标是在单独的Luancher
进程,是一个系统App
进程 -
Luancher
进程请求SystemServer
进程中的AMS
去创建应用的根Activity
(AndroidMnifest.xml
中initen-fifter
为Luanche
的activity
) -
AMS
通过包名让PMS
查询到相关应用信息,得到应用的Package
; -
AMS
创建activity
栈,根据Package
拿到根activity
的配置节点信息,放到栈中,此时栈中只有一个根activity
的配置节点信息,也就是在栈顶;(此处的栈不是应用层的栈,这个栈只是用来放activity
节点信息的) -
AMS
请求zygote
进程创建App
进程;zygote
进程比较特殊, 使用Socket
通信,而不是binder
;zygote
是所有应用的孵化器,zygote
进程挂掉时,手机会自动重启; -
zygote
进程去fork
出App
进程; -
APP
进程中的主线程调用ActivityThread.main()
静态函数,main
中创建ActivityThread
对象 -
接着在
ActivityThread.attch()
中创建了一个ApplicationThread
对象,作为和AMS
通信时,返回结果的桥梁; -
App
进程通过AMS
的binder
调用attachApplication(thread)
请求AMS
获取应用对应的Applaction
和栈顶中activity
节点信息(步骤4),此时给AMS
传过去了一个thread
,这个thread就是ApplicationThread
-
AMS
将从PMS
查到的application
节点数据序列化后,调用thread.bindApplaction
(data数据…)传给ActivityThread;
(此时代码还会继续往下执行,去获取栈顶activity
的节点信息) -
ActivityThread
调用sendMessage
发送消息BIND_APPLICATION(110)
给Handler
,Handler
调用handleBindApplication(data)
-
通过反射实例化
Instrumentation
对象:负责application
和activity
的生命周期的管理 -
通过
Instrumentation
对象反射实例化new Applaction
对象app -
调用
Instrumentation.callApplactionOnCreate(app)
-
执行
Applaction.onCreate()
-
步骤10中
AMS
继续向下执行查找activity
,AMS
将查到的栈顶根Activity
(LaunchActivity
)信息封装到一个事务ClientTransaction
中,提交事务并执行,在执行中,调用thread.scheduleTransaction
(事务数据);(thread
为ActivityThread
中的ApplicationThread
) -
在
ApplicationThread
回调scheduleTransaction
函数中,发送`EXECUTE_TRANSACTION(159)消息 -
Handler
处理EXECUTE_TRANSACTION
消息,从事务数据中取出LaunchActivity
信息,并调用hanldeLaunchActivity
(activity数据) -
通过
Instrumentation
对象反射实例化newActivity()
出对象activity -
执行
activity.attach()
,在attach
中创建WMS
的桥接代理类;(绘制流程会用到) -
通过
Instrumentation
调用callActivityOnCreate(activity)
-
执行
Activty.onCreate();
-
至此启动页根
Activity
启动完成;
下图中4-5中少了上面7-23的步骤:
7-15创建并启动了Application
;
16-22创建并启动了Activity
;
应用内activity
与activity
的跳转是跨进程通信,还是同一个进程内通信?
是跨进程通信;
跳转流程参考上面的:省去了application
的创建过程;
步骤3 +步骤16-23;
2.2.Android Framework
源码-PMS
Framework面试必问三:Zygote,AMS,PMS连环炮
SystemServer: Android
一切服务的启动者;
-
开机时,板子引导芯片启动引导程序
-
引导程序启动PID为0的
linux
内核进程 -
linux
系统启动init
脚本,启动PID永远为1的init进程 -
init
进程启动SystemManager
进程; -
SystemManager
进程启动完后; -
init
进程启动zygote
进程(native
进程) -
zygote
调用SystemServer.java
的main
函数,frok
出SystemServer
进程(java进程) -
SystemServer.java
的main
函数里执行SystemServer
的run
方法,main
函数里只有一句代码:new SystemServer().run();
-
run
方法中启动服务进程,AMS
、PMS
等
ps:SystemManager
: 是SystemServer
的叔叔,SystemServer
把所有服务都交给了SystemManager
管理;
-
AMS
、PMS
自身创建后,自身对象会添加到SystemManager
中,addService("key",AMS/PMS)
-
getService()
时,取的是个binder
;
PMS(PackageManagerService)
: 在SystemServer
的进程中,是SystemServer
中的一个对象;
有一个缓存中心:mPackages
;是一个Map
,key
为应用的包名,value
为每个应用的Package
;
在手机启动的时候,做了三件事,且只做一次:
-
遍历所有
app
文件 -
解压每个
apk
文件 -
dom
解析AndroidMnifest.xml
,并缓存;
作用:只解析每个Apk
中的AndroidMnifest.xml
中的信息,而不是去解析节点中每个xxxActivity.java
文件;解析到的信息缓存到mPackages
中,相当于“注册表”,方便之后AMS
快速定位到相应的APP
;
-
PackageManagerService.java
中会去两个目录做扫描scanDirTracedLI
:用户安装的所有APP目录sAppInstallDir:data/app/;
和系统应用所有APP的目录systemAppDir:System/app/
-
6.0-8.0都是单线程扫描,9.0和10.0是用线程池进行扫描,扫描到的apk文件信息,
new PackageParse(),
赋值给包解析工具类PackageParse
; -
解压
Apk
文件,9.0和10.0解析时会去判断缓存中是否有,有则用缓存,6.0-8.0没有使用缓存; -
使用工具类
PackageParse
解析AndroidMnifest.xml
,xml
解析完会返回Package
对象,每个APK
对应一个Package
对象,得到这个Package
对象后,缓存到PackageManagerService
的mPackages
这个ArrayMap
里;key
为应用的包名,value
为应用的Package
; -
Package
对象中有解析出的对应App
中的四大组件标签、权限标签等等,放入各自的List中,如:activites
、services
、revicers
、providers
、权限list
等等;activites
这些list
存的只是一个javabean
,而不是存的具体的应用层的Activity
;
解析AndroidMnifest.xml
流程:
-
打开
AndroidMnifest.xml
-
获取版本号、版本名称
-
判断
tagname=="applacation"
-
判断
tagname=="activity","reciver","service","provide"
等等 -
走到对应的解析
parseActivity,parseActivity
(reciver
和activity
的结构一样,就用同样的javabean
接收),parseService,parseProvide
-
解析完添加到
Package
的对应的list
中;
2.3.Android Framework
源码-IMS
Linux
事件机制:
事件都是储存在文件中;
如触摸屏幕事件:存储在dev/input/event0
的文件中,每次触摸都会以16进制数据储存;
INotify
:监听文件状态,有变化则产生FD值
epoll
机制:
epoll_create
:注册监听事件类型
epoll_ctl
:监听FD值,FD改变则唤醒epoll_wait()
epoll_wait
:没事件则阻塞,有事件则分发;
将INotify
和epoll
封装为一个对象EventHub
;
SystemServer
进程启动时,创建了InputManagerService
服务,这个IMS
在native
层创建了InputManager
对象;
InputManager
里有一个对象EventHub
;
同时InputManager
里面又开启了两个线程:
InputReaderThread
:不断去读取EventHub
里的事件;有事件时把数据封装后添加到队列,立马从队列里读取交给InputDispatcher
进行分发;
InputDispatcherThread:InputDispatcher
里面保存了wms
中所有的window
信息(wms
会将window
信息实时更新到InputDispatcher
中),InputDispatcher
就可以将事件分发给对应合适的window
;
App
进程中的ViewRootImpl
和SystemServer
中的IMS
通过socketpair
通信,因为事件产生的非常快且非常多使用binder
通信不适合
在ViewRootImpl
中setView后new了一个监听FD文件的回调,new WindowInputEventReceiver();
在回调中,底层使用epoll_ctl()
函数监听FD是否变化,有变化则会回调至java层,dispatchInputEvent();
这里就是Activity-》Viewgroup->View的事件分发前置;
底层事件信号传递总结:
-
事件信号都是用物理文件存储数据的,位置在
dev/input
文件夹下;touch
事件存储在dev/input/event0
的文件中; -
Linux
有提供相关的文件监控api
:inotify()
和epoll
机制 -
android
创建了一个封装了inotify()
和epoll
机制的对象EventHub
,来监控dev/input
文件夹下面的事件信号文件; -
android
自己启动两个线程来处理dev/input
文件夹下面的事件信号文件:InputReaderThread和
InputDispatherThread;` -
在
InputReaderThread
中开启循环,对EventHub
对象进行getEvent();
-
getEvent()
中有epoll_wait;
相当于wait-notif
机制;唤醒的触发点时dev/input
下的文件被改变; -
InputReaderThread
将dev/input
文件夹下面的事件信号文件数据进行 提取、封装,然后交给InputDispatherThread;
-
InputDispatherThread
最终选择到对应的ViewRootImpl(window)
进行分发数据; -
这里
App
进程和SystemServer
两个进程通过Socketpair
进行通信;两个进程一边一组socketpair
; -
在
ViewRootImpl
中对于Channel
连接的文件进行监控(epoll_ctr
),从而是上层接收到touch
信号;
2.4.Android WMS
及绘制流程
主角:ViewRootImpl
、Choreographer
、Surfaceflinfer
WMS
扮演了什么角色?
作为协调者,协调view
布局,绘制;
-
在
ActivityThread
中创建Actiivty
后,调用activity.attach()
时,创建一个窗体对象PhoneWindow
-
PhoneWindow
创建了一个WMS
的代理桥接类WindowManagerImpl
对象,作为WMS
在app
中的代表; -
WindowManagerImpl
对象中的(mGlobal)WindowManagerGlobal
专门和WMS
通信,在mGlobal
里面获取了到了WMS
的Binder
:getWindowSession()->WMS::openSession();
setContentView()
-
调用
PhoneWindow.setContentView(resouseID)
-
PhoneWindow
中:创建mDector
:窗体上的整个View
:里面有官方的主题布局+用户自己的布局; -
PhoneWindow
中:创建mContentParent
:官方主题布局中提供给用户装载布局的容器:id为content
; -
调用
mLayoutInflater.inflater(resouseID,mContentParent)
: -
解析用户的布局
xml
-
递归调用:解析根布局,通过反射创建根布局;解析子
view
,通过反射创建view
; -
最后
PhoneWindow
中的mContentParent
加载用户的根布局; -
提
view
数据
ps:这里递归调用,若嵌套层级太多,会导致栈溢出;因为递归调用不会释放栈;
ViewRootImpl
单例,管理所有View
的绘制策略;
注意onCreate.setContentView
后view数据已解析并实例化了;
-
在状态机为
Resume
时: -
调用
WindowManagerImpl
中的mGlobal.addView(view)
-
addView
中创建ViewRootImpl root=new ViewRootImpl()
: -
root.setView(view);
-
在
setView
总调用requestLayout()
-
requestLayout()
请求绘制,编舞者出场
帧速率: CPU/GPU出图速率;
刷新率: 屏幕刷新速率;
-
帧速率>刷新率时,出现丢帧(出图好多张了,但是只显示了开头和结尾两张,中间的丢了)
-
帧速率<刷新率,出现卡顿(屏幕刷新好多次了,但是还是显示的第一帧)
Vsync
: 垂直同步绘制信号; 因可能硬件帧速率和刷新率不一致,用来同步刷新的问题;
Choreographer
编舞者: 负责管理帧率节奏;
-
在内部维护了个
Handler
和Looper
,保证绘制发生在UI主线程:Looper.myLooper==mLooper
判断是否是主线程,是的话去调同步绘制信号,不是的话发送消息,走主线程去调同步绘制信号 -
走
native
层请求垂直同步信号,实际是找底层驱动要上次绘制的时间 -
请求到垂直同步信号后回调
onVsync
-
走
doFrame
去逻辑管控, 判断当前时间离上次绘制的时间大于了1帧的时间(16.66毫秒) 就跳帧(卡顿优化有用到),若小于16.66毫秒就再次请求垂直同步信号,防止重叠 -
执行
callback
,让ViewRootImpl
去真正绘制,调用ViewRootImpl.performTraversals()
真正的绘制: ViewRootImpl.performTraversals()
-
调用
relayoutWindow()
: -
创建用户
java
层的surface
:只有用户提供的画面数据; -
创建
native
层的surface
:包含用户提供的画面数据(java
层的surface
)+系统的画面数据(状态栏,电池、wifi等等); -
创建完
surface
后:依次调用:performMeasure(对应
view的
onMeasure)、
performLayout(onLayout)、
performDraw(onDraw);
在performDraw()
中:
-
将
view
的数据传至native
层的surface
-
surface
中的canvas
记录数据 -
生成
bitmap
图像数据(此时数据是在surface
中) -
将
surface
放入队列中;生产者消费者模式; -
通知
surfaceflinfer
进程去队列中取surface
数据 -
surfaceflinfer
拿到不同的surface
,进行融合,生成bitmap
数据 -
将
bitmap
数据放入framebuffer
中,进行展示
简单版总结: Activity.setContentView(R.layout.resId)
:
解析xml并实例化;
-
调用
phoneWindow.setContentView(resId)
-
在
setContentView
中调用installDector()
:根据不同的主题,找到系统默认的xml
,初始化出mDector
和mContentParent
(反射实例化出对应的ViewGroup
) -
初始化完成后,调用
mLayoutInflater.inflate(resId,mContentParent)
: -
解析
resId
的xml
文件,将解析的view
反射实例化;递归添加到各节点的viewgroup
中;最后将自己定义的xml
根布局view
添加到mContentParent;
绘制发生时间: 在AMS
回调ActivityThread
中的handleResumeActivity
时,也就是Resume
时,而不是onCreate()
;
-
获取
PhoneWindow
-
获取
PhoneWindow
中的mDector
布局视图view
-
将
mDector
布局视图view
传给ViewRootImpl
-
ViewRootImpl
中调用requestLayout()
-
requestLayout()
中依次调用:performMeasure()、performLayout()、performDraw()
💡三丶Framework
事件机制
3.1.Framework
事件机制—Android
事件处理的三种方法
3.1.1.背景
Android
的事件处理的三种方法:
1、基于监听的事件处理机制
setOnClickListener
,setOnLongClickListener
、setOnTouchListener
注意:如果onTouchEvent
方法return true
,则单击事件和长摁事件不再执行;若onLongClick
方法返回true
,则单击事件不再处理。
2、基于回调的事件处理机制
需要定义继承组件的类,重写回调方法Touch
方法执行时,先被Activity
捕获,DispatchTouchEvent
方法处理。return false
,交给上层的onTouchEvent
方法处理;return super.dispatchTouchEvent(ev)
,则传递给最外层的View
。
View
用Dispatch
方法处理,return false
,由上层的onTouchEvent
方法处理。如果返回super.dispatchTouchEvent(ev)
,则本层的onInterceptTouchEvent
拦截,如果拦截true,则拦截,false不拦截,传递给子View
的DispatchTouchEvent
处理。
常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent
(轨迹球事件)监听和回调同时存在时,先调用监听。
3.1.2.Android
基于监听
基于监听的时间处理机制模型
流程模型图
监听三要素:
Event source
事件源 Event
事件 Event Listener
事件监听器 下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:
- 点击时间( 由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个 )
- 触摸事件
归纳:
事件监听机制是一种委派式的事件处理机制,事件源(组件)事件处理委托给事件监听器 当事件源发生指定事件时,就通知指定事件监听器,执行相应的操作
常⽤监听接⼝
View.OnClickListener
单击事件监听器必须实现的接⼝ View.OnCreateContextMenuListener
创建上下⽂菜单事件 View.OnFocusChangeListener
焦点改变事件 View.OnKeyListener
按键事件监听器 View.OnLongClickListener
长按事件监听器 View.OnTouchListener
触摸屏事件监听器
- 基于监听的事件处理机制
⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。
事件监听器处理流程:
为事件源(例如:
button
)设置⼀个监听器,⽤于监听⽤户的操作(点击操作等)⽤户做出的操作触发事件源的监听器
⾃动⽣成对应的事件对象
将事件源对象作为参数传给事件监听器
事件监听器对事件对象进⾏判断,执⾏对应的事件处理器(处理⽅法)在此以
OnClickListener
单击事件为例使用intent来实现页面的跳转
内部类形式实现监听
<TextView
//id值
android:
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="内部类"
android:gravity="center"
android:textSize="20dp"
android:textColor="#fff"/>
public class MainActivity extends AppCompatActivity
//定义一个TextView对象
private TextView textView2;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得事件源
textView = findViewById(R.id.test2);
//确定事件为点击事件,绑定监听器到事件源
textView.setOnClickListener(new myListener());
//内部类实现页面跳转
private class myListener implements View.OnClickListener
@Override
public void onClick(View v)
//采用显示Intent启动第二个页面
startActivity(new Intent(MainActivity.this,internalActivity.class));
匿名内部类实现
<TextView
android:
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="匿名内部类"
android:gravity="center"
android:textSize="20dp"
android:textColor="#fff"/>
public class MainActivity extends AppCompatActivity
private TextView textView1;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得事件源
textView1 = findViewById(R.id.test3);
//匿名内部类实现跳转 (实现监听器,绑定监听器到事件源要同步进行)
textView1.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
startActivity(new Intent(MainActivity.this,anonymousActivity.class));
);
类本身实现监听器
<TextView
android:
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="类本身打开浏览器"
android:gravity="center"
android:textSize="20dp"
android:textColor="@color/colorWhite"/>
public class MainActivity extends AppCompatActivity implements View.OnClickListener
private TextView textView2;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得事件源
textView2 = findViewById(R.id.test4);
//绑定监听器到事件源
textView2.setOnClickListener(this);
//类本身实现 浏览器跳转
@Override
public void onClick(View v)
switch (v.getId())
case R.id.test4:
//采用隐式intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
break;
本节给大
以上是关于硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)的主要内容,如果未能解决你的问题,请参考以下文章
怒肝Framework通信篇合集;Handler+Binder+LiveData事件机制