疯狂Android讲义 - 学习笔记(三)

时间:2021-11-23 21:32:33

Android的事件处理

3.1 Android提供了两套事件处理机制:基于监听的事件处理、基于回调的事件处理。

3.2 基于监听的事件处理

3.2.1 监听的处理模型  主要涉及三类对象:EventSource、Event、EventListener。

Android的事件处理机制是一种委派式(Delegation)事件处理方式:普通组件将整个事件处理委托给特定的对象(事件监听器),可以把所有可能的事件授权给不同的事件监听器来处理,也可以让一类事件都使用同一个事件监听器来处理。

事件监听类是一个特殊的类,必须实现某Listener接口。

3.2.2 事件和事件监听器  如果事件足够简单、事件里封装的信息有限,那就无需封装事件对象、将事件对象出入事件监听器。但对于键盘事件、触摸屏事件,程序需要获取事件发生的详细信息,Android会把事件信息封装成xxxEvent对象,并传给事件监听器。

(源码\03\3.2\plane) 增加触摸事件:

planeView.setOnTouchListener(new View.OnTouchListener(){

@Override public boolean onTouch (View v, MotionEvent event)

......

说明:planeView所使用的事件监听器类是内部类,使用内部类可以在当前类中复用该监听器类;因为监听器类是外部类的内部类,所以可以*访问外部类的所有界面组件。这也是内部类的两个优势。

3.2.4 外部类作为事件监听器类  这种形式比较少见,因为不利于提高程序的内聚性,不能*访问创建GUI界面的类中的组件。但如果某个监听器确实需要被多个GUI界面所共享,而且主要完成某种业务逻辑的实现,则可以考虑用外部类的形式定义事件监听器类。

(源码\03\3.2\SendSms)

3.2.5 Activity本身作为事件监听器, 形式简单,但因为Activity的主要职责应该是完成界面初始化工作,界面类需要实现事件监听处理,比较奇怪,容易引起混乱。

3.2.6 匿名内部类作为事件监听器类, 这种形式是目前使用最广泛的事件监听器形式,因为事件处理器的复用价值不大。

3.2.7 直接绑定到标签,直接在界面布局文件中为指定标签绑定事件处理方法。

3.3 基于回调的事件处理

3.3.1 回调机制 对于这种事件处理模型,事件源和事件监听器是统一的,没有事件监听器了。Android为所有GUI组件都提供了一些事件处理的回调方法。继承GUI组件类,并重写该类的事件处理方法。

3.3.2 基于回调的事件传播 几乎所有基于回调的事件处理方法都有一个Boolean类型的返回值,如果返回true,表明该方法已完全处理该事件,该事件不会传播出去;如果返回false,表明该方法并未完全处理该事件,该事件会传播出去,会触发该组件所在Activity的回调方法。

3.3.3 对比

基于监听的事件处理模型具有的优势:分工明确,事件源、事件监听由两个类分开实现,由更好的可维护性;基于监听的事件监听器会被优先触发。

基于回调的事件处理机制的优势:可以更好地提高程序的内聚性。

3.4 响应系统设置的事件

监听系统设置的更改,对系统设置的更改做出响应。如:判断屏幕方向、系统方向的方向导航设备。

3.4.1 Configuration类 专门用户描述设备上的配置信息,既包括用户特定的配置项,也包括系统的动态设备配置。

3.4.2 重写Activity的onConfigurationChanged方法,响应系统设置更改,这个方法是基于回调的事件处理方法。

3.5 Handler消息传递机制

Android的UI操作不是线程安全的,Android的规则是:只允许主线程(UI线程,第一次启动时创建的) 修改Activity里的UI组件。如果新启动的线程需要动态修改界面组件的属性值,需要借助于Handler的消息传递机制来实现。

3.5.1 Handler类的主要作用:在新启动的线程中发送消息,在主线程中获取、处理消息。主线程只能通过回调的方式来适时处理其它线程发生的消息。(源码\03\3.5\HandlerTest)

2.5.2 Handler、Looper、MessageQueue的工作原理: 一个线程只能有一个Looper对象,Looper负责管理MessageQueue,Looper对象会创建MessageQueue,MessageQueue采用先进先出的方式管理消息;主UI线程中,系统为其初始化了一个Looper对象,因此程序直接创建Handler即可;程序员启动的子线程,必须自己创建一个Looper对象,并调用它的prepare()方法和loop()方法启动它即可。(源码\03\3.5\CalPrime)

尽量避免在UI线程中执行耗时操作,因为这可能导致一个“著名”的 异常:ANR异常,应用无法响应输入事件和Broadcast。Android默认约定当UI线程阻塞超过20秒将会引发ANR异常。

3.6 异步任务 AsyncTask

AsyncTask 适用于简单的异步处理,是轻量级的,不需要借助线程和Handler即可实现。

AsyncTask<Params, Progress, Result> 是抽象类,它定义了如下三种泛型类型:Params是启动任务执行的输入参数,Progress是后台任务完成的进度值,Result是后台任务完成后的返回结果。

使用AsyncTask需要创建它的子类,并根据需要实现相关方法。

(源码\03\3.6\AsyncTaskTest)

使用AsyncTask时必须遵守的规则:必须在UI线程中创建它的实例;必须在UI线程中调用它的execute()方法;AsyncTask的onPreExecute()、onPostExecute()等方法只能由Android系统负责调用;每个AsyncTask只能被执行一次。