万字长文新手入门教程(文章硬不硬核你说了算)
短短几个月,看着越来越多的读者通过我写的文章而关注公众号,我好开心,同时也诚惶诚恐,害怕无法持续输出高质量文章,让读者失望。但我知道,只有写出让读者心动的文章,才能得其心,会好好加油,为大家持续输出精品文章的。
本文分享Framework相关内容,提纲如下:
获取本文PDF版(视频 文字原稿),
私信: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 handlerHandler、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:有事件时就分发,没事件就阻塞
![](https://upload-images.jianshu.io/upload_images/15706246-2bf54a1171f33c30.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)
总结: 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);
- 完成了“主动”分发;
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源码-PMSFramework面试必问三: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中;
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信号;
主角: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()
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中的事件处理机制,例子中的是onClickListener点击事件,当然除了这个以外还有其他的事件,比如onItemClickListener,凡是需要通过setXxxListener这些,基本上都是基于事件监听的!
3.1.3.Android基于回调回调事件处理原理
监听事件处理是事件源与事件监听器分开的而基于回调的事件处理UI组件不但是事件源,而且还是事件监听器,通过组件的相关回调方法处理对应的事件。
回调事件应用步骤
Ⅰ. 自定义View类,继承自需要的View UI类。ex :自定义 MyButton按钮类 extends 基础Button类
Ⅱ. 复写回调函数。ex:public boolean onTouchEvent(MotionEvent event)
每一个事件回调方法都会返回一个boolean值,①.如果返回true:表示该事件已被处理,不再继续向外扩散,②.如果返回false:表示事件继续向外扩散
而说到基于回调就离不开监听机制。
回调机制与监听机制的区别:
如果说事件监听机制是⼀种委托式的事件处理,那么回调机制则恰好与之相反:对于基于回调机制的事件处理模型来说,事件源与事件监听器是统⼀的,或者说事件监听器完全消失了。
当⽤户在GUI组件上激发某个事件时,组件⾃⼰特定的⽅法将会负责处理该事件。
监听机制的事件源与事件监听是分开的。我们需要自己设置一个监听器,回调机制的事件源与事件监听是绑定在一起的。
- boolean类型
几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于表示该处理方法是否能完全处理该事件。如果处理事件的回调方法返回true,表明该处理方法已经完全处理改事件,该事件不会传播出去。如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。对于基于回调的时间传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。
实例:
MyButton 子类
public class MyButton extends AppCompatButton { public MyButton(Context context , AttributeSet set) { super(context , set); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { super.onKeyDown(keyCode , event); Log.v("-MyButton-", "the onKeyDown in MyButton"); // 返回false,表明并未完全处理该事件,该事件依然向外扩散 return true; } }
- MainActivity
public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bn = (Button) findViewById(R.id.bn); Log.v("-Listener-", "the onKeyDown in Listener"); // 为bn绑定事件监听器 bn.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View source , int keyCode, KeyEvent event) { // 只处理按下键的事件 if (event.getAction() == KeyEvent.ACTION_DOWN) { Log.v("-Listener-", "the onKeyDown in Listener"); } // 返回false,表明该事件会向外传播 return false; } }); } // 重写onKeyDown方法,该方法可监听它所包含的所有组件的按键被按下事件 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { super.onKeyDown(keyCode , event); Log.v("-Activity-" , "the onKeyDown in Activity"); //返回false,表明并未完全处理该事件,该事件依然向外扩散 return true; } }
这里是在模拟器里进行的测试,这里按下键盘(而不是点击),会看到 logcat 中的输出,如下:
V/-Listener-: the onKeyDown in Listener V/-MyButton-: the onKeyDown in MyButton V/-Activity-: the onKeyDown in Activity
举例:
- Override组件类的事件处理函数实现事件的处理。
View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。
比对
3.1.4.Handler消息处理
- 基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现。
- Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发。
- 某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。
什么是Handler
Handler是一个消息分发对象。
Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。
Handler的工作原理
在下面介绍Handler机制前,首先得了解以下几个概念:
1.Message 消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。 Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
2.Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
3.Looper 循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。 线程 UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。
Handler的运行流程
在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage,向消息队列中添加消息。 当通过 Looper.loop开启循环后,会不断地从消息池中读取消息,即调用 MessageQueue.next, 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage方法,接收消息,处理消息。
3.1.5.源码分析
在子线程创建Handler
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } } ; Looper.loop(); } }
从上面可以看出,在子线程中创建Handler之前,要调用 Looper.prepare()方法,Handler创建后,还要调用 Looper.loop()方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。
主线程的Looper
在ActivityThread的main方法,会调用 Looper.prepareMainLooper()来初始化Looper,并调用 Looper.loop()方法来开启循环。
3.1.5.Looper
public final class ActivityThread extends ClientTransactionHandler { // ... public static void main(String[] args) { // ... Looper.prepareMainLooper(); // ... Looper.loop(); } }
从上可知,要使用Handler,必须先创建一个Looper。
初始化looper:
public final class Looper { public static void prepare() { prepare(true); } private static void prepare(Boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } private Looper(Boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } // ... }
从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在 ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。
开启Looper
3.1.6.Handler
public final class Looper { // ... public static void loop() { // 获取TLS存储的Looper对象 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // 进入loop主循环方法 for (;;) { Message msg = queue.next(); // 可能会阻塞,因为next()方法可能会无线循环 if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " msg.target " " msg.callback ": " msg.what); } // ... final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { // 获取msg的目标Handler,然后分发Message msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } // ... msg.recycleUnchecked(); } } }
创建Handler
public class Handler { // ... public Handler() { this(null, false); } public Handler(Callback callback, Boolean async) { // ... // 必须先执行Looper.prepare(),才能获取Looper对象,否则为null mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " Thread.currentThread() " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; // 消息队列,来自Looper对象 mCallback = callback; // 回调方法 mAsynchronous = async; // 设置消息是否为异步处理方式 } }
发送消息:
子线程通过Handler的post()方法或send()方法发送消息,最终都是调用 sendMessageAtTime()方法。
post方法:
public final Boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0); } public final Boolean postAtTime(Runnable r, long uptimeMillis){ return sendMessageAtTime(getPostMessage(r), uptimeMillis); } public final Boolean postAtTime(Runnable r, Object token, long uptimeMillis){ return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } public final Boolean postDelayed(Runnable r, long delayMillis){ return sendMessageDelayed(getPostMessage(r), delayMillis); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
send方法
3.1.7.分发消息
public final Boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } public final Boolean sendEmptyMessage(int what){ return sendEmptyMessageDelayed(what, 0); } public final Boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } public final Boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } public final Boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() delayMillis); } sendMessageAtTime() public Boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private Boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
在loop()方法中,获取到下一条消息后,执行 msg.target.dispatchMessage(msg),来分发消息到目标Handler。
3.1.8.Handler的简单使用
public class Handler { // ... public void dispatchMessage(Message msg) { if (msg.callback != null) { // 当Message存在回调方法,调用该回调方法 handleCallback(msg); } else { if (mCallback != null) { // 当Handler存在Callback成员变量时,回调其handleMessage()方法 if (mCallback.handleMessage(msg)) { return; } } // Handler自身的回调方法 handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } }
在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。
3.2.Framework事件机制—onInterceptTouchEvent拦截流程解析3.2.1.基本知识
public class Activity extends android.app.Activity { private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 更新UI } } ; @Override public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { // 执行耗时任务 ... // 任务执行完后,通知Handler更新UI Message message = Message.obtain(); message.what = 1; mHandler.sendMessage(message); } } ).start(); } }
事件分发的三个函数
事件的分发 dispatchTouchEvent() 事件的拦截 onInterceptTouchEvent() 事件的处理(消费) onTouchEvent()
事件分发的对象
被分发的对象是那些?被分发的对象是用户触摸屏幕而产生的点击事件,事件主要包括:按下、滑动、抬起与取消。这些事件被封装成MotionEvent对象。
MotionEvent.ACTION_DOWN 在屏幕按下时 MotionEvent.ACTION_MOVE 在屏幕上滑动时 MotionEvent.ACTION_UP在屏幕抬起时 MotionEvent.ACTION_CANCLE 滑动超出控件边界时
分发事件的组件
分发事件的组件,也称为分发事件者,包括Activity、View和ViewGroup。它们三者的一般结构为:
3.2.2.事件处理流程
首先,我们需要了解事件处理中的几个方法:
1、在ViewGroup中,事件分为dispatchTouchEvent(事件的分发),onInterceptTouchEvent(事件的拦截),onTouchEvent(事件的处理)。
2、在View中,事件分为dispatchTouchEvent(事件的分发),onTouchEvent(事件的处理)。
下面是demo的界面结构,它是由两个自定义的ViewGroup和一个自定义的View组成,并分别重写了它们的以上几个方法。
其中 MyViewGroupA代码如下:
public class MyViewGroupA extends LinearLayout { public MyViewGroupA(Context context) { super(context); } public MyViewGroupA(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupA","onTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupA","onTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupA","onTouchEvent_ACTION_UP"); break; } return super.onTouchEvent(event); } }
MyViewGroupB代码如下:
public class MyViewGroupB extends LinearLayout { public MyViewGroupB(Context context) { super(context); } public MyViewGroupB(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyViewGroupB","onTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyViewGroupB","onTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyViewGroupB","onTouchEvent_ACTION_UP"); break; } return super.onTouchEvent(event); } }
MyView代码如下:
public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyView","dispatchTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyView","dispatchTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyView","dispatchTouchEvent_ACTION_UP"); break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("MyView","onTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyView","onTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyView","onTouchEvent_ACTION_UP"); break; } return super.onTouchEvent(event); } }
我们说过,事件传递是由上到下的,所以最外层的View首先对事件进行操作。而我们最外层是Activity,所以事件也是从这里开始。 Activity代码如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("Activity","dispatchTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("Activity","dispatchTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("Activity","dispatchTouchEvent_ACTION_UP"); break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i("Activity","onTouchEvent_ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("Activity","onTouchEvent_ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("Activity","onTouchEvent_ACTION_UP"); break; } return super.onTouchEvent(event); } }
现在我们通过触摸MyView开始进行分析。虽然dispatchTouchEvent是事件开始的第一步,但是在开发中,我们通常很少改写它,所以我们下面只讨论其他两个方法。 1、对以上方法均不作处理,都返回super。这意味着我们既不拦截,也不消费。
大家看输出结果:
I/Activity: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN I/MyView: dispatchTouchEvent_ACTION_DOWN I/MyView: onTouchEvent_ACTION_DOWN I/MyViewGroupB: onTouchEvent_ACTION_DOWN I/MyViewGroupA: onTouchEvent_ACTION_DOWN I/Activity: onTouchEvent_ACTION_DOWN I/Activity: dispatchTouchEvent_ACTION_MOVE I/Activity: onTouchEvent_ACTION_MOVE I/Activity: dispatchTouchEvent_ACTION_UP I/Activity: onTouchEvent_ACTION_UP
结合输出结果,我们可以总结出以下的结论:
image.png
结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发ACTION_DOWN的时候,事件会经过Activity——MyViewGroupA——MyViewGroupB——MyView一层层的向下进行dispatchTouchEvent(分发)—onInterceptTouchEvent(拦截)调用。当到达最底层MyView后,开始触发消费操作,因为我均不消费,ACTION_DOWN将由底层一层层向上冒,移交上层处理。当抵达最上层Activity后,说明下层均不消费,之后触发的ACTION_MOVE和ACTION_UP将不再向下层分发传递,直接交由Activity分发给自己进行处理。
2、我们将MyVIewGroupB的onInterceptTouchEvent返回值改为true,其他均是super。这意味着仅仅MyViewGroupB进行事件拦截,但均无消费
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: onTouchEvent_ACTION_DOWN I/MyViewGroupA: onTouchEvent_ACTION_DOWN I/Activity: onTouchEvent_ACTION_DOWN I/Activity: dispatchTouchEvent_ACTION_MOVE I/Activity: onTouchEvent_ACTION_MOVE I/Activity: dispatchTouchEvent_ACTION_UP I/Activity: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
当触发ACTION_DOWN的时候,事件依然是从Activity开始一层层向下传递,当传递到MyViewGroupB时,因为进行了事件拦截,所以执行完onInterceptTouchEvent后不再向下传递,而是直接交由MyViewGroupB的onTouchEvent进行消费处理。由于我们是只拦截,不消费,所以事件向上传递,交由上层处理,最终回到Activity。之后触发的ACTION_MOVE和ACTION_UP也不再向下传递,直接交由Activity分发给自己处理。
3、我们还是将MyViewGroupB的onInterceptTouchEvent返回super,但是将他的onTouchEvent返回true。这意味着我们不拦截,但是由MyViewGroupB进行事件处理。
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN I/MyView: dispatchTouchEvent_ACTION_DOWN I/MyView: onTouchEvent_ACTION_DOWN I/MyViewGroupB: onTouchEvent_ACTION_DOWN I/Activity: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupB: onTouchEvent_ACTION_MOVE I/Activity: dispatchTouchEvent_ACTION_UP I/MyViewGroupA: dispatchTouchEvent_ACTION_UP I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP I/MyViewGroupB: dispatchTouchEvent_ACTION_UP I/MyViewGroupB: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
可以看出,当触发ACTION_DOWN的时候,事件的分发传递过程和1的时候一样,从Activity开始一层层向下传递,最终传递到最底层MyView,触发消费操作,然后MyView将消费操作移交上层处理,然后到达MyViewGroupB的onTouchEvent,并且进行了消费处理,事件处理到此不在向上移交。当触发ACTION_MOVE和ACTION_UP操作时,事件依然需要由Activity开始向下分发传递,但是当传递到MyViewGroupB后,由于其消费了ACTION_DOWN,事件将不再继续向下分发,而是直接由MyViewGroupB分发给自己的onTouchEvent进行继续处理。事件处理也不再向上移交。
4、将MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改为true。这意味着既拦截,又消费。
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN I/MyViewGroupB: onTouchEvent_ACTION_DOWN I/Activity: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE I/MyViewGroupB: onTouchEvent_ACTION_MOVE I/Activity: dispatchTouchEvent_ACTION_UP I/MyViewGroupA: dispatchTouchEvent_ACTION_UP I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP I/MyViewGroupB: dispatchTouchEvent_ACTION_UP I/MyViewGroupB: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
当触发ACTION_DOWN的时候,依然从Activity开始向下传递,当到达MyViewGroupB的是,因为在onInterceptTouchEvent进行了拦截操作,因此不再继续向下分发传递,而是交由MyViewGroupB的onTouchEvent进行处理消费。MyViewGroupB的onTouchEvent返回的是true,说明它决定对ACTION_DOWN进行处理,因此事件也就不再移交上层处理。当触发ACTION_MOVE和ACTION_UP的时候,事件还是从Activity开始向下传递,当到达MyViewGroupB的时候,由于之前进行了拦截操作,因此,MyViewGroupB直接将事件分发给自己的onTouchEvent进行处理,不在向下分发传递。事件处理也不再向上层移交。
获取本文PDF版(视频 文字原稿),私信:Framework
,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com