文章目录
Android 四大组件:
- 活动activity
- 服务service
- 广播接收者Broadcast Receive
- 内容提供者Content Provider
一、四大组件
1、Activity活动
使用步骤
-
创建activity
-
创建布局
-
在Android Manifest文件中注册
- 配置主活动
<action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" />
生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-byj5aGbk-1673331437019)(C:\Users\HuangYingying\AppData\Roaming\Typora\typora-user-images\image-20221115172953014.png)]
一个activity启动, oncreate->onStart->onResume,就开始running了
activityA跳转到activityB,先activityA的onPause, 再会启动activityB的oncreate->onStart->onResume,接着才进行activityA的onStop;
如果再转换为activityA,B也是onPause, 接着A:onRestart->onStart->onResume(这是A没有被回收的情况以及B完全遮挡A的情况。如果A被回收了:那么A将会onCreate再走流程;如果A没有完完全被遮挡,那么A不会再onRestart和onStart)。然后B才onStop->onDestroy
启动活动
startActivity();
startActivityForResult();
启动其他活动:
startActivity(Intent intent);//启动其他活动
startActivityForResult(Intent intent, int requestCode);//以指定请求码启动activity,而且程序将会获取新启动的Activity返回的结果(通过重写onActivityResult()方法获取)
向另一个应用发送用户,使用Intent
创建 Intent
并设置额外信息后,请调用 startActivity()
以将其发送到系统
获取Activity的结果
活动的启动模式
-
standard,标准模式,同一活动可以启动多次,不管是否在栈顶
-
singleTop模式,若活动已在栈顶,则不会有新的实例活动
-
singleTask模式
- 启动目标Activity不存在,创建Activity实例,加入Task栈顶
- 启动目标Activity已经在Task栈顶,与singletop行为相同
- 存在Task栈中,但不是栈顶,系统会把该Activity上面所有Acitivity移除Task栈,使得目标Activity转入栈顶
-
singleInstance模式[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GH3Kiu2O-1673331437021)(C:\Users\HuangYingying\AppData\Roaming\Typora\typora-user-images\image-20221116105715387.png)]
事件监听器
通过事件处理,使UI和用户互动。
一般用Lambda ,比较简洁
基于回调的事件处理
事件监听机制是一种委托式的事件处理,回调恰恰相反:事件源和事件监听器是统一的,或者是事件监听器完全消失了,当用户在GUI组件上激发某个事件时,组件自己特定的方法会处理该事件。
事件传播
回调方法返回true,该事件不会传播出去;
返回false,会传播出去
优先级:先触发组件绑定的事件监听器,再出发回调方法。
2、service
生命周期:
手动调用startService()、stopService()、bindService()、unbindService()
自动调用:onCreate()、onstrartCommand()、onDestroy()…
被绑定服务的生命周期是在 为其他应用组件提供服务时,若接触绑定,绑定服务会解除
而被调用启动的生命周期是长期在后台的,与调用者无关。
创建服务,必须要创建Service的子类,重写回调方法,其中onBind必须重写
onBind() //组件想通过bindService()绑定服务时,会调用此方法
onCreate() //首次通过startService()启动服务时,会调用此方法
onStartcommand() //每次通过startService()启动服务时,会调用此方法
onDestroy() //销毁服务时
采用startService()开启服务:
onCreate()–> onStartCommand() --> onDestory();
采用绑定方式开启服务:
onCreate() —>onBind()—>onunbind()–>onDestory();
3、广播
标准广播
有序广播
4、contentProvider
二、view/布局
View和ViewGroup
TextView文本框、Button按钮、EditText编辑框、ImageView图片、ProgressBar进度条、AlertDialog警示弹窗(只能允许当前操作,屏蔽其他控件交互能力)、ProgressDialog(同AlertDialog,多了个进度)
基本布局
- 线性布局,LinearLayout
- 相对布局,RelaitiveLayout
- 帧布局,FrameLayout
- 百分比布局
ListView控件
借助适配器来完成
纵向滚动
RecyclerView控件
强化版ListView控件,功能更加完善,效率更加高
定义在了support库,使用的时候要添加依赖的库(在dependencies闭包中添加compile ‘com.android.support:recyclerview-v7:24.2.1’)
需要借助适配器来完成
新增横向滚动和瀑布流布局
addview()添加窗口且指定窗口层级
三、消息机制Handler
作用
- 在新启动的线程中发送消息
- 在主线程中获取、处理消息
handleMessage(Message msg);//处理消息的方法,常用于被重写
hasMessages(int what);//检查消息队列中是否包含what属性为指定值的消息
hasMessages(int what, Object object);//检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息
多个重载的Message obtainMessage();//获取消息
sendEmptyMessage(int what);//发送空消息
sendEmptyMessageDelayed(int what, long delayMillis);//指定多少毫秒后发送空消息
sendMessage(Message msg);//立即发送消息
sendMessageDelayed(Message msg, long delayMillis);//指定多少毫秒后发送消息
example code: 疯狂android讲义中3.5
部分源自博客
- Handler:快递员(属于某个快递公司的职员)
- Message:包裹(可以放置很多东西的箱子)
- MessageQueue:快递分拣中心(分拣快递的传送带)
- Looper:快递公司(具有处理包裹去向的管理中心)
Binder/Socket用于进程间的通讯,Handler用于同一进程的不同线程之间的通信
一个线程只能有一个Looper,一个MassageQueue,可以多个Handler
Handler只能绑定一个线程的Looper
-
Handler将Message发送到Looper的MessageQueue中
-
等待Looper的循环读取Message,处理Massage
-
调用Message的target,即附属的Handler的dispatchMessage()de方法,然后完成更新UI操作。
sendMessage(),发送消息
dispatchMessage(), Looper发回消息
①Handler的创建、方法
Handler的构造方法做的事
- 确认Handler所在线程的Looper对象,mLooper = Looper.myLooper();
- 若Looper不空,获取Looper的消息队列,赋值,mQueue = mLooper.mQueue;
- 设置Calback处理消息回调,mCallback = callback;
②MessageQueue
Handler发送来的Message在MessageQueue中,选择一个合适的位置插入
③Looper
将消息取出,通过Handler的dispatchMessage()发送回去给Handler
主线程中:系统已经初始化,直接创建Handler
子线程中:要自己创建一个Looper对象
四、Binder机制
进程间的通信
Linux下的是共享内存、管道、Socket、消息队列、信号等方式,但是在Android上看不到,所以需要用Binder机制来进行
Binder的功能
- 用驱动程序来推进进程间的通信
- 通过共享内存来提高性能
- 为进程请求分配每个进程的线程池
- 针对系统中的对象引入了引用计数和跨进程的对象引用映射
- 进程间同步调用
AIDL
AIDL支持的原语类型
- Java中的所有原语类型int、long…
- String
- CharSequence
- List(List中的元素是以上列表中支持的数据类型)
- Map
基础类型步骤
- 在服务端创建了一个.aidl文件,定义好接口,服务端的.aidl和客户端的.aidl要完全一致
package com.example.android.process1;
interface Aidl {
int Sum(int num);
}
- 在服务端实现.aidl中的接口,要继承自Stub(要先编译,才会生成.aidl对应的.java文件)
package com.example.android.process1;
import android.os.RemoteException;
import android.util.Log;
public class AidlImpl extends Aidl.Stub {
String TAG = "AidlImpl";
@Override
public int Sum(int num) throws RemoteException {
Log.d(TAG, "num:" + num);
Log.d(TAG, "Sum: ");
return (num+1)*num/2;
}
}
- 在服务端的Service中,把aidl的接口暴露出去
package com.example.android.process1;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
public MyService() {
}
private AidlImpl binder = new AidlImpl();
@Override
public IBinder onBind(Intent intent) {
Log.d("AidlImpl", "onBind.....");
return binder;
}
}
- 在客户端中bindservice
public class MainActivity extends AppCompatActivity {
public static String TAG = "MainActivity";
private Aidl service;//声明服务端
private ServiceConnection conn = new ServiceConnection() {//绑定成功后会回调
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "onServiceConnected: Service Connected");
service = Aidl.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "onServiceConnected: Service Connected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: MainActivity");
setContentView(R.layout.activity_main);
Button bt = findViewById(R.id.bt1);
//绑定服务
Intent intent = new Intent();
ComponentName cp = new ComponentName("com.example.android.process1", "com.example.android.process1.MyService");
intent.setComponent(cp)
.setPackage(getPackageName());
bindService(intent, conn, Context.BIND_AUTO_CREATE);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
if(service == null) return ;
int ans = service.Sum(10);//rpc,远程调用,和调用本地方法一致
Log.d(TAG, "ans: " + ans);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
两个进程间通信需要对应的aidl接口,且需一致。后来,为了方便,不用每次修改就去对应一 一修改,就把他提出来,写成了.aidl文件
target:
// aidl 数据类型 √
// 传输数据的大小 √
// 死亡监听 √
// 序列化 √
// 线程同步
// 重连问题(挂掉重连)
IPC传递对象步骤(Parcelable序列化
- 服务端中定义好实体类,继承自parcelable,(该实现的实现,没实现会有提醒,可定义好成员变量后一键生成)
- 再服务端中,在aidl文件夹下建立2个.aidl文件
- 用于定义接口,注意参数前有个in
// Aidl.aidl
package com.example.android.process1;
import com.example.android.process1.userMessageAIDL;
import com.example.android.process1.userMessage;
// Declare any non-default types here with import statements
interface Aidl {
List<userMessage> getuserMessageListIn(in userMessage um);
}
- 固定是parcelable+实体类类名
package com.example.android.process1;
parcelable userMessage;
- 在服务端的Service里面暴露出去
package com.example.android.process1;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class MyService extends Service {
public MyService() {
}
public AidlImpl binder = new AidlImpl();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
- 将服务端的实体类,aidl文件夹原封不动的copy到客户端中,注意包名要完全一致
- 一样的绑定方法,将实体类对象
AIDL权限问题
Android IPC之AIDL使用(二)线程同步、权限验证 - 掘金 (juejin.cn)
出于安全考虑。
五、Intent
Action、Category属性与intent-filter配置
- 一个Intent对象最多只能包括一个Action属性,程序可以通过调用Intent的setAction(String str)方法来设置Action属性值;
- 但一个Intent对象可以包括多个Category属性,程序可调用Intent的addCategory(String str)方法来为Intent添加Category属性。
Data、Type属性与intent-filter配置
Data属性接收一个Uri对象,格式:scheme://host:port/path
六、同步Synchronous和异步Asynchronous
同步和异步关注的是消息通信机制(synchronous communication/ asynchronous communication)。同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反,调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回调函数来处理这个调用
七、模式
MVC
MVP
MVVM
Model, View, ViewModel
-
MVVM应该改成M-VM-V会更容易直观地理解。View-Model作为胶水层,把视图View和数据模型Model粘合在一起。
-
MVVM不是一个纯前端的架构模式。它适用于所有的包含GUI(Graphical User Interface 图形用户接口)的应用程序中(包含后端部分)。
-
MVVM其实可以细分为M-C-VM-V的四层架构。
-
对于以上M-C-VM-V层的理解:
-
- M(odel)层:定义数据结构,建立应用的抽象模型。
- C(ontroller)层:实现业务逻辑,向上暴露数据更新的接口,调用Model层来进行模型数据的增删改查,以达到应用数据更新的目的。
- V(iew)-M(odel)层:将Model层的抽象模型转换为视图模型用于展示,同时将视图交互事件绑定到Controller层的数据更新接口上。
- V(iew)层:将视图模型通过特定的GUI展示出来,并在GUI控件上绑定视图交互事件。
-
说白了,对于一款拥有GUI的应用程序来说,用户与计算机进行交流的过程,不过是IO(输入输出)的过程。计算机通过输出设备(显示器、扬声器、机械马达等。不过这里我们针对于图形接口来讲的话,一般就是显示器)将视图数据进行展示,用户通过输入设备(键盘、鼠标、触摸板等)来触发特定的事件达到模型的更新。
-
我们之所以要发明这种分层架构,最主要的原因是为了让Model层和Controller层能够复用。甚至于对于同一款应用程序在不同的GUI上进行展示时,View-Model层也是复用的,仅仅只是把View层进行了替换而已。
-
再拓展一下,假如我们的应用程序需要在非GUI界面进行实现,而是通过其他UI方式来实现呢?只需要将View-Model层替换成新的UI-Model,再与新的UI进行桥接,同样的功能便可以跨UI进行实现了。
-
对于上述这点,举个例子:针对于残障人士(比如盲人),我们的应用程序应该更加方便易用。或许我们需要考虑使用扬声器来代替显示器进行输出,同时使用麦克风来进行输入。这时,我们可以将上述的View-Model替换为Audio-Model作为语音模型,UI层即Audio层用于播放语音和接收语音输入。
-
综上所述,对于UI应用程序(给用户提供了用户接口的应用程序),都可以抽象成M-I-IM(其中I指Interface)架构模式来达到模型、逻辑、表征之间的分离解耦,并提高开发效率。
八、组件
Databinding
自动绑定UI的框架。
Livedata
LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
作用
- 实时刷新数据
- 防止内存泄露
使用
- 创建LiveData实例,指定源数据类型
- 创建Observer实例,实现onChanged()方法,用于接收源数据变化并刷新UI
- LiveData实例使用observe()方法添加观察者,并传入LifecycleOwner
- LiveData实例使用setValue()/postValue()更新源数据 (子线程要postValue())
public class LiveDataTestActivity extends AppCompatActivity{
private MutableLiveData<String> mLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecycle_test);
//liveData基本使用
mLiveData = new MutableLiveData<>();
mLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "onChanged: "+s);
}
});
Log.i(TAG, "onCreate: ");
mLiveData.setValue("onCreate");//activity是非活跃状态,不会回调onChanged。变为活跃时,value被onStart中的value覆盖
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart: ");
mLiveData.setValue("onStart");//活跃状态,会回调onChanged。并且value会覆盖onCreate、onStop中设置的value
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
mLiveData.setValue("onResume");//活跃状态,回调onChanged
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
mLiveData.setValue("onPause");//活跃状态,回调onChanged
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
mLiveData.setValue("onStop");//非活跃状态,不会回调onChanged。后面变为活跃时,value被onStart中的value覆盖
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
mLiveData.setValue("onDestroy");//非活跃状态,且此时Observer已被移除,不会回调onChanged
}
}
Lifecycle
-
LifeCycle是一个抽象类,用来存储生命周期状体的;
-
是Android Jetpack-Architecture(架构组件)中的一员
-
LifeCycle—对组件的解耦;
-
Jetpack提供了2个类:LifecycleOwner(被观察者), LifecycleObserver(观察者)。
-
通过观察者模式,实现对页面生命周期的监听
-
Lifecycle 的实现机制是观察者模式
Lifecycle类
跟踪生命周期的流程如下:(某个state发生event的时候,状态变化图)
例子:(观察Activity的生命周期)
-
继承DefaultLifecycleObserver,自定义观察者类
class MyObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { // 执行业务逻辑 } override fun onPause(owner: LifecycleOwner) { // 执行业务逻辑 } ... }
-
在Activity中向生命周期添加观察者;
getLifeCycle().addObserver(MyObserver());//有getLifecycle()这个方法,是因为高版本Fragment和Activity已实现LifecycleOwner接口
class MainActivity: AppCompatActivity{ @Override fun onCreate(){ getLifecycle().addObserver(MyObserver()) } }
LifecycleOwner接口
(Activity和Fragment组件已经默认实现了此接口)
LifecycleOwner接口只有一个方法getLifecycle(), 返回的是Lifecycle对象。实现了这个接口,就表示这个类是具有生命周期的。
如果想管理整个APP进程的生命周期,则需要用ProcessLifecycleOwner类
自定义类实现LifecycleOwner接口
class MyObject : LifecycleOwner {
private lateinit var mLR: LifecycleRegistry
fun onCreate {
mLR = LifecycleRegistry(this)
// 设置此时状态
mLR.markState(Lifecycle.State.CREATED)
}
fun onStart() {
// 设置此时状态
mLR.markState(Lifecycle.State.STARTED)
}
/** 返回生命周期实例 */
override fun getLifecycle(): Lifecycle = mLR
}
Viewmodel
九、Fragment
Activity片段
Fragment要继承Fragment基类。
onCreate();//系统创建Fragment对象后回调该方法
onCreateView();//当Fragment绘制界面组件时会回调该方法,该方法必须返回一个View,该View也就是该Fragment所显示的View
onPause();//当用户离开该Fragment时会回调此方法
Fragment和Activity通信
为了在Activity中显示Fragment,要把Fragment添加到Activity
- 在布局文件中使用<fragment…/>元素添加Fragment,<fragment…/>元素的android.name属性指定Fragment的实现类
- 在代码中通过FragmentTransaction对象的add()方法来添加Fragment。
getSupportFragmentManager()方法可返回FragmentManager, FragmentManager对象的beginTransaction()方法可以开启并返回FragmentTransaction对象。
Fragment管理,Fragment事务
FragmentManager:
Activity管理Fragment主要靠FragmentManager
FragmentTransaction:
代表了Activity对Fragment执行的多个改变操作。
生命周期
- 运行状态,位于前台,用户可见,可获得焦点
- 暂停状态,其他Activity位于前台,Fragment位于前台,不可获得焦点
- 停止状态,Fragment不可见,失去焦点
- 销毁状态,Fragment完全被删除/该Fragment所在的Activity被结束
onAttach() 在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)
onCreateView() 当Fragment 创建视图时调用
onActivityCreated() 在相关联的 Activity 的 onCreate() 方法已返回时调用。
onDestroyView() 当Fragment中的视图被移除时调用
onDetach() 当Fragment 和 Activity 取消关联时调用。
Activity和Fragment生命周期关联
- Activity就是一个大页面,Fragment是嵌套在Activity里,所以,Fragment先生成,然后有了Activity的页面,才有Fragment的页面,Activity先运行,Fragment再运行。
- Fragment先暂停,Activity再暂停,停止也是如此
- 重启动时Activity先重启动,Fragment再启动,运行也是Activity先运行
- 销毁的话,是都停止之后,Fragment先销毁,Activity再销毁。
十、Context
十一、多线程
多线程的应用在Android开发中是非常常见的,常用方法主要有:
- 继承Thread类
- 实现Runnable接口
- Handler
- AsyncTask
- HandlerThread
Runnable
定义
Runnable接口是线程辅助类,仅定义了一个方法run()方法
作用
实现多线程
优点
灵活:Runnable可以继承其他类实现对Runnable实现类的增强,避免了Thread类由于继承Thread类而无法继承其他类的问题
共享资源:Runnable接口的run()方法可以被多个线程共享,适用于多个进程处理一种资源的问题
使用方法
- 实现Runnable接口
- 重写run()方法
- 创建runnable实例
- 创建Thread实例
- 将Runnable实例放入Thread实例中
- 通过线程实例控制线程的行为(运行,停止),在运行时会调用Runnable接口中的run方法。
注意:Java中真正能创建新线程的只有Thread类对,通过实现Runnable的方式,最终还是通过Thread类对象来创建线程
HandlerThread
Android多线程:手把手教你使用HandlerThread - 简书 (jianshu.com)
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
十二、设计模式
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
十三、Android事件分发机制
事件传递返回值:onInterceptTouchEvent
true:拦截,不继续传递
false:不拦截,继续传递
事件处理返回值:onTouchEvent
true:处理了,不上报
false:处理了,但上报
MotionEvent
1、MotionEvent事件是什么
**点击事件(Touch事件)**会封装成MotionEvent对象
传递对象
Activity、ViewGroup、View
Activity(ViewGroup(View))
事件分发顺序
Activity->ViewGroup->View
事件分发方法
2、事件分发机制流程
Activity事件分发机制
Android事件分发机制首先会将点击事件传递到Activity中,具体是执行dispatchTouchEvent()进行事件分发。
/**
* 源码分析:Activity.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 仅贴出核心代码
// ->>分析1
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
// 否则:继续往下调用Activity.onTouchEvent
}
// ->>分析3
return onTouchEvent(ev);
}
/**
* 分析1:getWindow().superDispatchTouchEvent(ev)
* 说明:
* a. getWindow() = 获取Window类的对象
* b. Window类是抽象类,其唯一实现类 = PhoneWindow类
* c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现
*/
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
// mDecor = 顶层View(DecorView)的实例对象
// ->> 分析2
}
/**
* 分析2:mDecor.superDispatchTouchEvent(event)
* 定义:属于顶层View(DecorView)
* 说明:
* a. DecorView类是PhoneWindow类的一个内部类
* b. DecorView继承自FrameLayout,是所有界面的父类
* c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
*/
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
// 调用父类的方法 = ViewGroup的dispatchTouchEvent()
// 即将事件传递到ViewGroup去处理,详细请看后续章节分析的ViewGroup的事件分发机制
}
// 回到最初的分析2入口处
/**
* 分析3:Activity.onTouchEvent()
* 调用场景:当一个点击事件未被Activity下任何一个View接收/处理时,就会调用该方法
*/
public boolean onTouchEvent(MotionEvent event) {
// ->> 分析5
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
// 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false,分析完毕
}
/**
* 分析4:mWindow.shouldCloseOnTouch(this, event)
* 作用:主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
*/
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
// 返回true:说明事件在边界外,即 消费事件
return true;
}
// 返回false:在边界内,即未消费(默认)
return false;
}
ViewGroup事件分发机制
从上面Activity的事件分发机制可知,在Activity.dispatchTouchEvent()实现了将事件从Activity->ViewGroup的传递,ViewGroup的事件分发机制从dispatchTouchEvent()开始。
/**
* 源码分析:ViewGroup.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 仅贴出关键代码
...
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// 分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
// 判断值1-disallowIntercept:是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
// 判断值2-!onInterceptTouchEvent(ev) :对onInterceptTouchEvent()返回值取反
// a. 若在onInterceptTouchEvent()中返回false,即不拦截事件,从而进入到条件判断的内部
// b. 若在onInterceptTouchEvent()中返回true,即拦截事件,从而跳出了该条件判断
// c. 关于onInterceptTouchEvent() ->>分析1
// 分析2
// 1. 通过for循环,遍历当前ViewGroup下的所有子View
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
// 2. 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// 3. 条件判断的内部调用了该View的dispatchTouchEvent()
// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面章节介绍的View事件分发机制)
if (child.dispatchTouchEvent(ev)) {
// 调用子View的dispatchTouchEvent后是有返回值的
// 若该控件可点击,那么点击时dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
// 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
// 即该子View把ViewGroup的点击事件消费掉了
mMotionTarget = child;
return true;
}
}
}
}
}
}
...
return super.dispatchTouchEvent(ev);
// 若无任何View接收事件(如点击空白处)/ViewGroup本身拦截了事件(复写了onInterceptTouchEvent()返回true)
// 会调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此会执行ViewGroup的onTouch() -> onTouchEvent() -> performClick() -> onClick(),即自己处理该事件,事件不会往下传递
// 具体请参考View事件分发机制中的View.dispatchTouchEvent()
...
}
/**
* 分析1:ViewGroup.onInterceptTouchEvent()
* 作用:是否拦截事件
* 说明:
* a. 返回false:不拦截(默认)
* b. 返回true:拦截,即事件停止往下传递(需手动复写onInterceptTouchEvent()其返回true)
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 默认不拦截
return false;
}
// 回到调用原处
View事件分发机制
/**
* 源码分析:View.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if ( (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener != null &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
// 1. (mViewFlags & ENABLED_MASK) == ENABLED
// 2. mOnTouchListener != null
// 3. mOnTouchListener.onTouch(this, event)
// 下面对这3个条件逐个分析
/**
* 条件1:(mViewFlags & ENABLED_MASK) == ENABLED
* 说明:
* 1. 该条件是判断当前点击的控件是否enable
* 2. 由于很多View默认enable,故该条件恒定为true(除非手动设置为false)
*/
/**
* 条件2:mOnTouchListener != null
* 说明:
* 1. mOnTouchListener变量在View.setOnTouchListener()里赋值
* 2. 即只要给控件注册了Touch事件,mOnTouchListener就一定被赋值(即不为空)
*/
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
/**
* 条件3:mOnTouchListener.onTouch(this, event)
* 说明:
* 1. 即回调控件注册Touch事件时的onTouch();
* 2. 需手动复写设置,具体如下(以按钮Button为例)
*/
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
// 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)
// onTouchEvent()源码分析 -> 分析1
}
});
/**
* 分析1:onTouchEvent()
*/
public boolean onTouchEvent(MotionEvent event) {
... // 仅展示关键代码
// 若该控件可点击,则进入switch判断中
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
// 根据当前事件类型进行判断处理
switch (event.getAction()) {
// a. 事件类型=抬起View(主要分析)
case MotionEvent.ACTION_UP:
performClick();
// ->>分析2
break;
// b. 事件类型=按下View
case MotionEvent.ACTION_DOWN:
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
// c. 事件类型=结束事件
case MotionEvent.ACTION_CANCEL:
refreshDrawableState();
removeTapCallback();
break;
// d. 事件类型=滑动View
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
removeLongPressCallback();
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
// 若该控件可点击,就一定返回true
return true;
}
// 若该控件不可点击,就一定返回false
return false;
}
/**
* 分析2:performClick()
*/
public boolean performClick() {
if (mOnClickListener != null) {
// 只要通过setOnClickListener()为控件View注册1个点击事件
// 那么就会给mOnClickListener变量赋值(即不为空)
// 则会往下回调onClick() & performClick()返回true
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
3、总的来说
4、MotionEvent对应的ACTION
- ACTION_DOWN
- ACTION_MOVE
- ACTION_UP
- ACTION_CANCEL
KeyEvent
一个键的down或者up,都会创建一个keyevent对象来对应这个操作
相关API:
KeyEvent:
- int ACTION_DOWN = 0:标识down的常量
- int ACTION_UP = 1:标识up的常量
- int getAction():得到事件类型
- int getKeyCode():得到按键的keycode(唯一标识)
- startTracking():追踪事件,用于长按监听
Activity:
- boolean dispatchKeyEvent(KeyEvent event):分发事件
- boolean onKeyDown(int keyCode, KeyEvent event):按下按键的回调
- boolean onKeyUp(int keyCode, KeyEvent event):松开按键的回调
- boolean onKeyLongPress(int keyCode, KeyEvent event):长按按键的回调
Android按键事件处理流程 – KeyEvent - xiaoweiz - 博客园 (cnblogs.com)
KeyEvent的分发流程非常简单,那就是直接给当前获取了焦点的View,谁有焦点,KeyEvent就给谁。
流程:
Android Tv 中的按键事件 KeyEvent 分发处理流程 - 请叫我大苏 - 博客园 (cnblogs.com)
Android KeyEvent分发与焦点切换 - 掘金 (juejin.cn):解决了以上链接中未解决的问题
Model内部数据流向:
创建过程:
数据更新过程:
Request过程
View和ViewModel绑定,