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相关的函数有
上述代码中核心的一句是: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系列:
Send系列:
首先来看一下Post和Send的区别:post的参数是Runnable,也就是可执行的线程。而send系列的参数则为Message消息。post内部会将Runnable包装成Message形式。
那么如果封装呢?先看一下Message的内部属性。
Looper内部有一个MessageQueue消息队列,Looper就像一个发动机,驱动着整个程序的运行。
在整个系统中Handler,Looper,MessageQueue,Message的对应关系如下:
1.每个Thread只对应着一个Looper;
2.每个Looper只对应着一个MessageQueue;
3.每个MessageQueue中可以有多个Message;
4.每个Message最多对应一个Handler。
看一下Looper创建MessageQueue的源码:
下面来介绍两种线程:普通线程和主线程ActivityThread(也叫UI线程)
普通线程:
也就是说我们自己创建一个Thread,然后创建Handler和Looper,并建立他们之间的关系。
步骤有三个:1.Looper的准备工作(prepare);
2.Handler的创建;
3.Looper开始运转。
使用该线程中的Handler:
接着研究执行new Handler()时,是如何将该Handler绑定到该Thread和Looper的。
Handler的构造函数有下面几种:
UI主线程:ActivityThread
下面是一个在UI主线程中使用Handler的Demo。
UI主线程中的Handler经常用于UI的更新,普通线程我在项目中的应用是后台线程进行一些配置文件的下载,网络请求等,其中的Handler用于线程之间的通信。其实Android为我们提供了一种简便的普通线程使用Handler:HandlerThread。Android关于该类的描述:
1.线程的创建和开始;
下面结合一个实际项目工程:在程序启动后创建一个后台工作线程,来完成用户的登录和配置文件的下载,并通过Handler来完成线程之间的通信,实现对UI的更新。
View.post(Runnable action)
Runable中的run方法是在UI线程中执行的,Runable不一定是新开了一个线程执行。该函数的工作原理如下:
在View的post方法中获得UI线程的一个Handler,将该Rnnable包装成Message添加到MessageQueue中。
不管三者之间的关系有多复杂,其总体思路是: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中。
相关文章推荐
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等!!!!!!!!!!
- 在Android中使用Handler和Thread线程执行后台操作
- Silverlight实用窍门系列:23.Silverlight多线程技术Thread的应用,后台线程更新UI控件,向多线程传递参数【附带源码实例】
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android中使用Handler和Thread线程执行后台操作
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask
- 线程 (Handler、Looper、MessageQueue和Thread的理解)
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- 在Android中使用Handler和Thread线程执行后台操作
- 23.Silverlight多线程技术Thread的应用,后台线程更新UI控件,向多线程传递参数
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等【转】
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等