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

       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中。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值