您的位置:首页 > 运维架构

Thread+Looper+Handler原理及项目中后台线程的应用

2015-02-03 17:37 357 查看
Handler在我刚做开发时就接触过,但是当时对其与Looper,MessageQueue,Thread之间的关系非常模糊。我觉得很多Android开发者,尤其是初学者,对Handler的原理都不是很清楚。由于最近的项目中使用Handler比较多,所以对其原理进行了较深的研究。那么就写篇博客来总结一下Handler的原理,然后写了一个项目实例应用Handler,这个实例中使用了多个线程进行通信,这样自己的整体思路才能更清楚点,也发现了不足的地方。

不管三者之间的关系有多复杂,其总体思路是:Looper源源不断的从MessageQueue中取出Message,然后Handler来处理该Message。

handler的两个功能:处理Message和将某个Message添加到MessageQueue中。

1.处理Message

与处理Message相关的函数有

public void dispatchMessage(Message msg); //对Message进行分发
public void handleMessgae(Message msg);//对Message进行处理
分发Message是由Looper执行的。我们来看看Looper.loop的代码。
public static void loop(){
final Looper me=myLooper();//获得Looper对象
...
final MessageQueue queue=me.mQueue();//获取Looper中与之唯一对应的MessageQueue
...
for(;;){//不断进行循环处理
Message msg=queue.next();//取出队列中的下一个消息
if(msg==null) return;
...
msg.target.dispatchMessage(msg);
...
msg.recycle();//消息处理完成后,进行回收
}
}

上述代码中核心的一句是:msg.target.dispatchMessage(msg) 。Message对象中保存了处理它的Handler的引用,这样就可以调用该Handler的dispatchMessage(msg)函数来分发消息Message了。

Handler内部的分发流程是这样的:

1.Message.callback(Runnable对象)是否为空,若不为空,优先执行Runnable。否则执行步骤2。

2.mHandler.mCallback是否为空,若不为空,优先执行mCallback。否则执行步骤3。

3.在Message.callback(Runnable对象)和mHandler.mCallback均为空的情况下,再执行mHandler.handleMessage(msg);

我们可以在创建Handler时重载handleMessage来实现我们自己处理Messgae的过程。

2.将某个Message添加到MessageQueue中

刚开始我也不清楚为什么Handler要处理Message但又把Message加到消息队列MessageQueue中。其实这样做的目的是保证处理的有序性,把所要需要处理的Message添加到消息队列中,之后再一个一个从消息队列中取出处理。

那Handler是如何将Message添加到MessageQueue中的呢?相应的函数有下面两种:Post系列和Send系列。

Post系列:

final boolean post(Runnable r);   //将一个Runnable对象包装成Message后加入到消息队列中
final boolean postAtTime(Runnable r, long uptime);//将一个Runnable对象包装成Message后,在指定的时间添加到消息队列中

Send系列:

final boolean sendEmptyMessage(int what) ;//发送类型为what的Message,该Message会有handleMessage(msg)来处理。
final boolean sendMessageAtFrontOfQuene(Message msg);
final boolean sendMessageAtTime(Message msg, long uptimeMillis); //在指定时间将Message添加到消息队列中。
final boolean sendMessageDelayed(Message msg, long delayMillis) ;//延长delayMillis时间后在发送Message,其内部的实现是:计算出发送时                                                                    间=当前时间+延长时间delayMillis,然后再调用sendMessageAtTime

首先来看一下Post和Send的区别:post的参数是Runnable,也就是可执行的线程。而send系列的参数则为Message消息。post内部会将Runnable包装成Message形式。

那么如果封装呢?先看一下Message的内部属性。

public final class Message implements Parcelable{
public int what;
public int arg1;
public int arg2;
public Object obj;
....
Handler target; //处理该Message的Handler
Runnable callback;//回调
}
其内部属性中的Handler是目标Handler,Looper在分发Message时根据该属性来分发给指定的Handler。还有一个属性:Runnable callback。在执行post(Runnable对象)时,就是将该Runnable对象赋值给了callback。我们来看一下Android内核源码中的实现。
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r),0);
}
private static Message getPostMessage(Runnable r){
Message m=Message.obtain();//从Message池中获取一个Message,这样可以避免资源浪费
m.callback=r;
return m;}



Looper内部有一个MessageQueue消息队列,Looper就像一个发动机,驱动着整个程序的运行。

在整个系统中Handler,Looper,MessageQueue,Message的对应关系如下:

1.每个Thread只对应着一个Looper;

2.每个Looper只对应着一个MessageQueue;

3.每个MessageQueue中可以有多个Message;

4.每个Message最多对应一个Handler。

看一下Looper创建MessageQueue的源码:

final MessageQueue mQueue;
//Looper的构造函数
private Looper(boolean quitAllowed){
mQueue=new MessageQueue(quitAllowed);
....
}


下面来介绍两种线程:普通线程和主线程ActivityThread(也叫UI线程)

普通线程:

也就是说我们自己创建一个Thread,然后创建Handler和Looper,并建立他们之间的关系。

步骤有三个:1.Looper的准备工作(prepare);

2.Handler的创建;

3.Looper开始运转。

class MyLooperThread extends Thread{

public Handler mHandler;
public void run(){
Looper.prepare();
mHandler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 0:
Log.i(TAG,"MyLooperThread.Handler is handling Message");
break;
}
}
};
Looper.loop();
}
}

使用该线程中的Handler:

MyLooperThread myThread=new MyLooperThread();
myThread.start();
myThread.mHandler.sendEmptyMessage(0);
我们需要研究一下上面自定义线程中的Thread和Looper以及Handler是如何建立关系的。从上面短短的几行代码中我们可以猜想(其实事实也是这样)在Looper.prepare()中建立了Thread和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));
}
当我们在使用Looper时,肯定要 import android.os.Looper;在导入该包时Looper中就会创建一个静态的全局变量。
static final ThreadLocal<Looper> sThreadLocal =new ThreadLocal<Looper>();
该变量sThreadLocal的特殊性在于:它只能被当前线程所访问,即使是同一进程中的其他线程也无法访问,那么这就保障了Thread与Looper的一一对应关系,且在执行Looper.prepare()时,会为当前线程创建唯一的Looper。
接着研究执行new Handler()时,是如何将该Handler绑定到该Thread和Looper的。

Handler的构造函数有下面几种:

public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper,Callback callback);
这些参数用来对Handler内部的成员变量赋值。
public class Handler{
...
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
...
}
new Handler()的执行代码如下:
public Handler() {
this(null, false);
}

public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到执行Handler()时,Handler内部引用的mLooper即为该线程中的Looper,引用的mQueue为mLooper中的MessageQueue,这样就将Handler,Looper和Thread三者之间建立了关联。

UI主线程:ActivityThread

下面是一个在UI主线程中使用Handler的Demo。

public class MainActivity extends ActionBarActivity {

public final static String TAG="MianActivity";

private Handler mHandler=new Handler(){
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what){
case 0:
Log.i(TAG, "Message.what=0");
break;
case 1:
Log.i(TAG, "Message.what=1");
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessage(0);
mHandler.post(new Runnable(){
@Override
public void run() {
Log.i(TAG, "This is a new Runnable");//可能会引起线程堵塞
}
});
}

}
UI主线程的Looper建立是在ActivityThread创建时内部完成。Handler与Looper,ActivityThread建立联系的方式与普通线程类似,也是在new Handler()内部完成的。到这里整个Handler+Looper+MessageQueue+Thread的原理就总结完成了。
UI主线程中的Handler经常用于UI的更新,普通线程我在项目中的应用是后台线程进行一些配置文件的下载,网络请求等,其中的Handler用于线程之间的通信。其实Android为我们提供了一种简便的普通线程使用Handler:HandlerThread。Android关于该类的描述:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
HandlerThread的使用步骤:

1.线程的创建和开始;

HandlerThread myThread=new HandlerThread("Thread_name");
myThread.start();
2.Looper的创建(第二步经常和第三步一起执行);
Looper myLooper=myThread.getLooper();
3.Handler的创建;
Handler mHandler=new Handler(myLooper){
public void handleMessage(Message msg){
}
};
如果想退出HandlerThread,调用HandlerThread.quit()函数。
下面结合一个实际项目工程:在程序启动后创建一个后台工作线程,来完成用户的登录和配置文件的下载,并通过Handler来完成线程之间的通信,实现对UI的更新。

package com.example.testhandler;

import java.io.File;
import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

public class MainActivity extends ActionBarActivity {

public static final String TAG="MianActivity";
public static final int READY_LOGIN=0x0001;//准备登录
public static final int ON_LOGIN=0x0002;//正在登录
public static final int DOWNLOAD_CONFIG=0x0003;//下载配置文件
public static final int SUCCESS_LOGIN=0x0004;//登陆成功

private TextView text;//显示当前工作状态
private String userAccount;//用户账号
private String userPassword;
private HandlerThread m_workThread;
private Handler m_workHandler;

//UI主线程的Handler:更新UI
private Handler mHandler=new Handler(){
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what){
case DOWNLOAD_CONFIG:
text.setText("下载配置文件中...");
break;
case ON_LOGIN:
text.setText("正在登陆中...");
break;
case SUCCESS_LOGIN:
text.setText("完成登录");
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text=(TextView)findViewById(R.id.text);

//创建并启动后台工作线程
m_workThread=new HandlerThread("workThread");
m_workThread.start();
//将m_workThread线程的Loop和MessageQueue关联到Handler
m_workHandler=new Handler(m_workThread.getLooper()){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what){
case READY_LOGIN:
//准备登录包括:用户账号获取和配置文件的下载
userAccount="123456";
userPassword="123456";
File file=new File(MainActivity.this.getFilesDir()+"/baseconfig.xml");
if(!file.exists()){
//配置文件不存在,UI提示正在下载配置文件,再创建一个线程来下载文件
mHandler.obtainMessage(DOWNLOAD_CONFIG).sendToTarget();
downloadConfig();
}
break;
case ON_LOGIN:
mHandler.sendMessage(mHandler.obtainMessage(ON_LOGIN));//更新UI
startLogin(userAccount,userPassword);
break;
case SUCCESS_LOGIN:
mHandler.sendMessage(mHandler.obtainMessage(SUCCESS_LOGIN));//更新UI
break;
}
}
};
//在后台工作线程中完成登录过程
m_workHandler.obtainMessage(READY_LOGIN).sendToTarget();
}

private void downloadConfig(){
//启动一个新的线程下载配置文件
new Thread(){
@Override
public void run(){

//...这里进行一系列Http请求完成下载
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

m_workHandler.sendMessage(m_workHandler.obtainMessage(ON_LOGIN));//完成下载后通知后台线程开始登陆
}
}.start();
}

private void startLogin(String account,String password){
//...这里完成Socket登陆过程
new Thread(){
public void run(){
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//完成后更新UI
m_workHandler.sendMessage(m_workHandler.obtainMessage(SUCCESS_LOGIN));
}
}.start();
}
}

View.post(Runnable action)

Runable中的run方法是在UI线程中执行的,Runable不一定是新开了一个线程执行。该函数的工作原理如下:

在View的post方法中获得UI线程的一个Handler,将该Rnnable包装成Message添加到MessageQueue中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐