Handler Android应用开发消息机制

来源:互联网 发布:odysseus windows 编辑:程序博客网 时间:2024/05/17 04:03

一、背景

当应用程序启动时,Android首先会开启一个主线程(即UI线程),主线程为管理界面中的UI控件、进行事件分发,比如:你要点击一个Button,Android会分发事件到Button上来响应你的操作。如果此时需要一个耗时操作,例如:联网读取数据或读取本地一个较大文本时,你不能把这些操作放在主线程中。如果你放在主线程中,界面会出现假死现象,如果5s后还没有完成的话则会收到Android系统的一个错误提示“强制关闭”。此时,我们则要把这些耗时的操作放在一个子线程中,因此此子线程涉及到了UI更新,而更新UI只能在主线程中更新,这时我们就需要引入Handler来实现该操作。它与子线程可以通过Message对象来传递消息,这个时候Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)的Message对象(里面包含数据且该消息对象会放在主线程的消息队列MQ中),然后在主线程中处理这些消息从而可以对UI控件进行更新等操作。

二、Android的消息处理有三个核心类:Looper、Handler和Message

1、Looper

Looper使一个普通的线程变成Looper线程。Looper线程就是循环工作的线程,创建looper线程代码如下:

public class MyLoopThread extends Thread {@Overridepublic void run() {Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue//.....其他处理,如实例化Handler等Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理}}

效果图如下:

              

注:一个线程(Thread)最多有且只能有一个Looper对象,该Looper对象内部包含了一个消息队列MessageQueue

由上面代码可以知道Looper对象的prepare()方法将会初始化当前线程为looper线程,并且实例化Looper和初始化消息队列;

而Looper的loop()方法则是循环取消息,并分发到对应的hangdler处理消息,原理如下图:


Looper类除了prepare()和loop()方法外,还有其他的方法,如下:

Looper.myLooper()    :得到当前线程的Looper对象;

getThread()       :得到Looper对象所属的线程

quit()       :结束looper循环

2、Handler消息处理(异步处理处理)

主要接受子线程发送的消息,并用此数据配合主线程更新UI

解释:Handler扮演了往MQ上添加消息和处理消息(只处理自己发出的消息)的角色,即:通知MQ要要执行一个任务(sendMessage),并在looper.loop()方法关联到此Handler时执行任务(handlerMessage())然后Handler处理消息,整个过程是异步执行的。

注:因为Handler创建时会关联一个looper,默认的构造方法将关联到当前线程的looper,所以上面说的:“looper.loop()方法关联到此Handler”可以理解为handler创建时关联的looper是执行looper.loop()方法的looper线程的looper。所以,Handler关联的looper是可以设置的,可以为子looper线程的looper也可以是主线程(UI线程——也是一个looper线程)的looper,Handler关联的looper是哪个线程的looper它就会在那个线程执行(即:Handler的执行线程只与它关联的looper有关,与它实例化的位置无关)。Handler的实例化代码如下:

public class MyAndroidStudyMainActivity extends Activity {private Handler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_my_android_study_main);mHandler = new Handler(getMainLooper(), new Callback() {//实例化Handler并关联主线程的looper,当new Callback()//前面没有参数时实例化的Handler关联的则是当前所处线程的looper@Overridepublic boolean handleMessage(Message msg) {//.......处理消息(耗时操作)return true;}});}}
将Handler加入到looperThread线程类中时,则可以异步处理消息了(如:在子线程中实例化Handler时关联UI线程的looper,我们在子线程中联网取数据后把数据发送给Handler,然后Handler处理消息就可以更新UI界面了)。结构代码如下:

public class MyLoopThread extends Thread {private Handler mHandler1;private Handler mHandler2;@Overridepublic void run() {Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue//实例化两个HandlermHandler1 = new Handler();mHandler2 = new Handler();Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理}}
效果图如下:


由效果图可见:一个线程最多有且只能有一个looper,但可以有多个Handler。
上面说的在子线程更新UI界面则可以实现了,结构代码如下:

public class MyLoopThread extends Thread {private Handler mHandler1;@Overridepublic void run() {Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue//实例化HandlermHandler1 = new Handler(getMainLooper(),new Callback() {@Overridepublic boolean handleMessage(Message msg) {//......更新UI界面return true;}});Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理}}
【例】如下图所示,要在子线程中把界面上TextView控件的内容修改


XML文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@android:color/holo_blue_dark"    android:gravity="center"    android:orientation="vertical" >    <TextView        android:id="@+id/message_txt"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:text="我是启动界面"        android:textColor="@android:color/white"        android:textSize="24sp" />    <Button        android:id="@+id/send_btn"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="发送消息到主线程执行" /></LinearLayout>

Java代码:

public class HandlerActivity extends Activity {private MyLoopThread myLooper;//定义子线程private TextView mMessageTxt;//定义TextViewprivate Button mSendMsgBtn;//定义Buttonprivate int count = 1; // 标识消息@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题setContentView(R.layout.activity_handler_layout);mMessageTxt = (TextView) findViewById(R.id.message_txt);mSendMsgBtn = (Button) findViewById(R.id.send_btn);myLooper = new MyLoopThread();//实例子线程myLooper.start();//启动子线程mSendMsgBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Message msg = Message.obtain();msg.what = 11;msg.arg1 = count++;myLooper.mHandler1.sendMessage(msg);//Handler发送消息}});}/** * 消息在主线程执行的情况 消息在子线程执行的情况 *  */public class MyLoopThread extends Thread {public Handler mHandler1;@Overridepublic void run() {Looper.prepare();  //实例Looper,初始化消息队列MessageQueuemHandler1 = new Handler(getMainLooper(), new Callback() {@Overridepublic boolean handleMessage(Message msg) {//Handler处理消息,更新UIint arg1 = msg.arg1;mMessageTxt.setText("主线程执行 :  消息 " + arg1);return true;}});Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理}}}
运行程序后如下图:



我们可以看见,当点击Button后TextView的内容已经被修改了,所以,Handler能够实现在子线程中对UI界面的更新操作。


0 0
原创粉丝点击