前语
因为很多小伙伴在前前后后发现了一个问题,在很多大厂面试的时分基本上都会问到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根底篇大合集,根基安定高楼平地起
收拾不易,重视一下吧。开端进入正题,ღ( ・ᴗ・` )