您的位置:首页 > 移动开发 > Android开发

Handler Android应用开发消息机制

2015-10-21 21:09 567 查看
一、背景

当应用程序启动时,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 {

@Override
public 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;
@Override
protected 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
@Override
public 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;
@Override
public void run() {
Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue
//实例化两个Handler
mHandler1 = new Handler();
mHandler2 = new Handler();
Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理

}
}
效果图如下:



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

public class MyLoopThread extends Thread {
private Handler mHandler1;
@Override
public void run() {
Looper.prepare();  //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue
//实例化Handler
mHandler1 = new Handler(getMainLooper(),new Callback() {

@Override
public 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;//定义TextView
private Button mSendMsgBtn;//定义Button
private int count = 1; // 标识消息

@Override
protected 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() {

@Override
public 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;

@Override
public void run() {
Looper.prepare();  //实例Looper,初始化消息队列MessageQueue
mHandler1 = new Handler(getMainLooper(), new Callback() {
@Override
public boolean handleMessage(Message msg) {//Handler处理消息,更新UI
int arg1 = msg.arg1;
mMessageTxt.setText("主线程执行 :  消息 " + arg1);
return true;
}
});
Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理
}
}
}
运行程序后如下图:



我们可以看见,当点击Button后TextView的内容已经被修改了,所以,Handler能够实现在子线程中对UI界面的更新操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: