Framework大合集,从里到外分析的明明白白(下)
前言
由于很多小伙伴在前前后后发现了一个问题,在很多大厂面试的时候基本上都会问到Framework这方面,刚好之前整理的 《十大模块手册》 刚好有这方面的内容,拿出来分享一下,主要借鉴,错误的地方一起改正。😂
国庆回来上班第二天,不摸鱼了😑
关注公众号:Android苦做舟 解锁 《Android十大板块文档》,让学习更贴近未来实战。已形成PDF版
内容如下:
1.2022最新Android11位大厂面试专题,128道附答案 2.音视频大合集,从初中高到面试应有尽有 3.Android车载应用大合集,从零开始一起学 4.性能优化大合集,告别优化烦恼 5.Framework大合集,从里到外分析的明明白白 6.Flutter大合集,进阶Flutter高级工程师 7.compose大合集,拥抱新技术 8.Jetpack大合集,全家桶一次吃个够 9.架构大合集,轻松应对工作需求 10.Android基础篇大合集,根基稳固高楼平地起
整理不易,关注一下吧。开始进入正题,ღ( ´・ᴗ・` ) 🤔
四丶Framework底层服务
1.Android Framework源码-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 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.Android Framework源码-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中;
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信号;
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编舞者: 负责管理帧率节奏;
- 在内部维护了个Haner和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事件机制
1.Framework事件机制—Android事件处理的三种方法
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
(轨迹球事件)监听和回调同时存在时,先调用监听。
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这些,基本上都是基于事件监听的!
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时重写这些事件处理方法即可。
public` `interface` `Callback { ` ` ``// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于 ` ` ``// 标识该处理函数是否能完全处理该事件 ` `// 返回true,表明该函数已完全处理该事件,该事件不会传播出去 ` `// 返回false,表明该函数未完全处理该事件,该事件会传播出去 ` ` ``boolean` `onKeyDown(``int` `keyCode, KeyEvent event); ` ` ``boolean` `onKeyLongPress(``int` `keyCode, KeyEvent event); ` ` ``boolean` `onKeyUp(``int` `keyCode, KeyEvent event); ` ` ``boolean` `onKeyMultiple(``int` `keyCode, ``int` `count, KeyEvent event); ` `} ` `public` `interface` `Callback {`` ``// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于`` ``// 标识该处理函数是否能完全处理该事件``// 返回true,表明该函数已完全处理该事件,该事件不会传播出去``// 返回false,表明该函数未完全处理该事件,该事件会传播出去`` ``boolean` `onKeyDown(``int` `keyCode, KeyEvent event);`` ``boolean` `onKeyLongPress(``int` `keyCode, KeyEvent event);`` ``boolean` `onKeyUp(``int` `keyCode, KeyEvent event);`` ``boolean` `onKeyMultiple(``int` `keyCode, ``int` `count, KeyEvent event);``}public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
比对
- 基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现。
- Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发。
- 某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。
1.4.Handler消息处理
什么是Handler
Handler是一个消息分发对象。
Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。
Handler的工作原理
在下面介绍Handler机制前,首先得了解以下几个概念:
1.Message 消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。 Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
2.Handler 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()
方法来开启循环。
public final class ActivityThread extends ClientTransactionHandler {
// ...
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
// ...
Looper.loop();
}
}
1.5.Looper
从上可知,要使用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
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();
}
}
}
1.6.Handler
创建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方法
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);
}
1.7.分发消息
在loop()
方法中,获取到下一条消息后,执行 msg.target.dispatchMessage(msg)
,来分发消息到目标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();
}
}
1.8.Handler的简单使用
在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。
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();
}
}
2.Framework事件机制—onInterceptTouchEvent拦截流程解析
2.1.基本知识
事件分发的三个函数
事件的分发
dispatchTouchEvent()
事件的拦截onInterceptTouchEvent()
事件的处理(消费)onTouchEvent()
事件分发的对象
被分发的对象是那些?被分发的对象是用户触摸屏幕而产生的点击事件,事件主要包括:按下、滑动、抬起与取消。这些事件被封装成MotionEvent对象。
MotionEvent.ACTION_DOWN 在屏幕按下时 MotionEvent.ACTION_MOVE 在屏幕上滑动时 MotionEvent.ACTION_UP 在屏幕抬起时 MotionEvent.ACTION_CANCLE 滑动超出控件边界时
分发事件的组件
分发事件的组件,也称为分发事件者,包括Activity、View和ViewGroup。它们三者的一般结构为:
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
结合输出结果,我们可以总结出以下的结论:
结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发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
进行处理,不在向下分发传递。事件处理也不再向上层移交。
关注公众号:Android苦做舟 解锁 《Android十大板块文档》,让学习更贴近未来实战。已形成PDF版
内容如下:
1.2022最新Android11位大厂面试专题,128道附答案 2.音视频大合集,从初中高到面试应有尽有 3.Android车载应用大合集,从零开始一起学 4.性能优化大合集,告别优化烦恼 5.Framework大合集,从里到外分析的明明白白 6.Flutter大合集,进阶Flutter高级工程师 7.compose大合集,拥抱新技术 8.Jetpack大合集,全家桶一次吃个够 9.架构大合集,轻松应对工作需求 10.Android基础篇大合集,根基稳固高楼平地起
整理不易,关注一下吧。开始进入正题,ღ( ´・ᴗ・` ) 🤔
转载自:https://juejin.cn/post/7152434348992495624