尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38408493

作为Android开发者,Handler这个类应该是再熟悉只是了。由于差点儿不论什么App的开发。都会使用到Handler这个类,有些同学可能就要说了,我全然能够使用AsyncTask取代它,这个确实是能够的,可是事实上AsyncTask也是通过Handler实现的,详细的大家能够去看看源代码即可了,Handler的主要功能就是实现子线程和主线程的通信。比如在子线程中运行一些耗时操作。操作完毕之后通知主线程跟新UI(由于Android是不同意在子线程中跟新UI的)。

以下就使用一个简单的样例開始这篇文章吧

public class MainActivity extends Activity
{
public static final int MSG_DOWNLOAD_FINISH=0X001;
//创建一个Handler的匿名内部类
private Handler handler=new Handler()
{ @Override
public void handleMessage(Message msg)
{
switch(msg.what)
{
case MSG_DOWNLOAD_FINISH:
Log.v("yzy", "handler所在的线程id是-->"+Thread.currentThread().getName());
break;
}
} }; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} //启动一个下载线程
public void download(View view)
{
new Thread()
{
public void run() {
try
{
Log.v("yzy", "下载子线程的Name是--->"+Thread.currentThread().getName());
//在子线程运行,模拟一个下载任务
Thread.sleep(2000);
//下载完毕后,发送下载完毕消息
handler.sendEmptyMessage(MSG_DOWNLOAD_FINISH);
} catch (InterruptedException e)
{
e.printStackTrace();
}
};
}.start();
}

运行结果:
08-03 16:31:46.418: V/yzy(30486): 下载子线程的Name是--->Thread-22588
08-03 16:31:48.421: V/yzy(30486): handler所在的线程Name是-->main

上面样例就是模拟了一个下载任务,当下载完毕后,通过Handler对象发送一个消息,最后被handlerMessage方法接收并处理。通过运行结果能够发现。download方法是在子线程中完毕的。而handlerMessage是在UI线程中被调用的。

到了这里一个简单的Handler使用案例就结束了,假设你已经明确了上面的代码,那么说明你已经明确了Handler的最基本使用。
以下再来一个简单的样例:

public void postRun(View view)
{
new Thread(){
public void run() {
try
{
Log.v("yzy", " 下载子线程的Name是--->"+Thread.currentThread().getName());
Thread.sleep(2000);
handler.post(new Runnable()
{ @Override
public void run()
{
Log.v("yzy", "handler post run -->"+Thread.currentThread().getName());
}
});
} catch (InterruptedException e)
{
e.printStackTrace();
} }; }.start(); }

运行结果:
08-03 16:31:52.528: V/yzy(30486):  下载子线程的Name是--->Thread-22589
08-03 16:31:54.531: V/yzy(30486): handler post run -->main

在Handler中除了能够发送Message对象还能够发送Runnable对象。从运行结果来看发送的Runnable也是在main线程中运行的
那么是不是通过handler发出的Message和Runnable都是在UI线程中运行的呢,这个可不一定。如今我在onCreate方法中创建一个handler2

HandlerThread thread=new HandlerThread("yzy");
thread.start(); handler2=new Handler(thread.getLooper())
{
public void handleMessage(Message msg) {
switch(msg.what)
{
case MSG_DOWNLOAD_FINISH:
Log.v("yzy", "handler所在的线程Name是-->"+Thread.currentThread().getName());
break;
} };
};

然后将上面代码中使用handler的地方换为handler2,然后运行代码:代码中创建了一个名称为"yzy"的线程。然后再yzy线程中初始化了handler2
结果例如以下:
08-03 17:07:10.571: V/yzy(8224): 下载子线程的Name是--->Thread-23005
08-03 17:07:12.574: V/yzy(8224): handler所在的线程Name是-->yzy

我们发现通过handler2发出的Message和Runnable都是在yzy线程中运行的。

假设对于上面的代码和运行结果你都知道原因。那么说明你已经对Handler的使用非常熟悉了。假设不清楚上面的运行结果,那么请往下继续吧

開始分析原因之前我想提出以下几个问题:
1、Handler发出的Message和Runnable是怎样传递到UI线程的?
2、在什么情况下通过Handler发出的Message和Runnable不会传递到UI线程?
3、为什么我是在HandlerThread初始化一个Handler?

以下我们就逐一解决上述问题吧,在解决这个问题之前我们须要分析一个Hanlder这个类的源代码。

首先看看Handler的构造函数,它有好几个构造函数。我们一个个的看吧

//无參构造函数
public Handler() {
//检查Handler是否是static的,假设不是的。那么有可能导致内存泄露。详细能够百度
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//非常重要的一个属性。临时就将Looper理解成一个消息队列的管理者吧。用来从消息队列中取消息的,稍后会分析这个类
mLooper = Looper.myLooper();
if (mLooper == null) {
//这个异常非经常见哦。Handler一定要在有Looper的线程上运行,这个也就是为什么我在HandlerThread中初始化Handler
//而没有在Thread里面初始化,假设在Thread里面初始化须要先调用Looper.prepare方法
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//将mLooper里面的消息队列拷贝到自身的mQueue,这也就意味着Handler和Looper是公用一个消息队列
mQueue = mLooper.mQueue;
//回调函数默认是Null
mCallback = null;
} //这个和上面一样的。仅仅只是传入了一个回调函数
public Handler(Callback callback) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
} 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;
} /**
* 传入一个Looper,并和Looper公用一个消息队列
*/
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
} /**
* 和上面一个几乎相同,就不在赘述
*/
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}

看了Handler的构造函数。发现Hanlder里面有一个消息队列,这个消息队列是从Looper里面传入的,而且Handler须要在有Looper的线程中调用。

既然它和Looper关系这么紧密,我们看看Looper究竟是什么东西吧。打开Looper的源代码,顶部看到这种一个样例:

*  class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
*

这个就是为了说明Handler仅仅能在有Looper的线程中创建,UI线程是有Looper的。对于没有Looper的线程,须要调用Loope.prepare,Looper.loop等函数,如上例。

那我们正式进入Looper的源代码看看吧

//Looper的构造函数,主要是对消息队列等属性初始化
private Looper() {
mQueue = new MessageQueue();
mRun = true;
//记录所在线程
mThread = Thread.currentThread();
} //在上面的样例中,看到当在一个没有Looper的线程中创建Handler,就须要运行这个函数,
//这个函数主要是new 一个Looper,燃火放入ThreadLocal中保存。做到一个线程就创建一次。 第二次调用这种方法会抛出异常的
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
} //prepare是创建并保存。这种方法就是取出Looper
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
} //这种方法是给系统调用的,UI线程通过调用这个线程,从而保证UI线程里有一个Looper
//须要注意:假设一个线程是UI线程,那么myLooper和getMainLooper是同一个Looper,通过这个代码非常好理解
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
} //获得UI线程的Looper,通常我想Hanlder的handleMessage在UI线程运行时一般会new Handler(getMainLooper());
public synchronized static final Looper getMainLooper() {
return mMainLooper;
} /**
* 上面的样例是运行了这种方法的,这种方法是一个死循环。一直从消息队列中读取消息,并分发出去
*/
public static final void loop() {
//得到本线程的Looper
Looper me = myLooper();
//拿到消息队列
MessageQueue queue = me.mQueue;
while (true) {
//从消息队列中拿一个消息
Message msg = queue.next(); // might block
//取到了一个消息
if (msg != null) {
//这个一般不等于Null,通常就是发出这个Message的Handler
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
//调用Handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
//将消息放入消息池
msg.recycle();
}
}
} //返回Looper的消息队列
public static final MessageQueue myQueue() {
return myLooper().mQueue;
}

看了Looper的源代码之后总结一下:
  在一个线程的run方法里面先调用prepare方法。主要保证了该线程里面有了一个Looper对象,假设在这个线程里面创建一个Hanlder,那么看看上面Handler的空的构造方法。Handler里面的Looper就是线程里面保存的Looper,从而能够创建的Handler和线程公用一个Looper,也就是公用一个消息队列。

说的更详细一点就是Hanlder和Looper所在的线程公用一个消息队列。

最后调用loop方法。这个线程不断从消息队列取消息。然后调用Hanlder的dispatchMessage方法。
  
最后看看Handler的其它几个比較重要的方法吧

//在Handler中发送一个消息到消息队列。相似的方法非常多,我仅仅选了这一个
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//将target设置成了Handler
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
//这种方法是在loop方法中被调用的
public void dispatchMessage(Message msg) {
//首先检查msg中callback(是一个Runnable对象)是否为Null,假设不为null,则直接调用callback中的run方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//检查Handler中的callback是否为空,假设不为空,则直接调用callback中的handleMessage,假设返回TRUE,则直接返回不在调用Handler中的handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//最后调用Handler中的handlerMessage
handleMessage(msg);
}
}

在前面的样例中,我们以前使用过Handler中的post方法,我们看看它是怎么实现的

 public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
} private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
} public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

我想假设前面明确了的,这个就不用我说了的吧。

事实上在Activity中有一个方法runOnUiThread,就是使用这个原理实现的。例如以下:

//让Runnable在UI线程运行
public final void runOnUiThread(Runnable action) {
//假设当前线程不是UI线程。那么通过Handler转发到UI线程
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

View里面也有相似的方法:

public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
// Assume that post will succeed later
ViewRoot.getRunQueue().post(action);
return true;
} return handler.post(action);
}

原理是上面是一样的。

依据上面的学习,能够得出结论:
  1、假设Handler是在UI线程创建的。那么Handler和UI线程共享消息队列,所以通过Hanlder发出的消息都是在UI线程中运行的
  2、假设要让Handler发出的消息在子线程中运行,非常easy,在创建Handler的时候传入子线程的Looper(通过Looper.prepare创建)
  到这里相信在前面我提及的三个问题都已经有答案了吧,假设有没有明确的,欢迎留言。。。。

Android Handler消息机制深入浅出的更多相关文章

  1. Android Handler消息机制不完全解析

    1.Handler的作用 Android开发中,我们经常使用Handler进行页面的更新.例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我 ...

  2. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  3. Android Handler 消息机制原理解析

    前言 做过 Android 开发的童鞋都知道,不能在非主线程修改 UI 控件,因为 Android 规定只能在主线程中访问 UI ,如果在子线程中访问 UI ,那么程序就会抛出异常 android.v ...

  4. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  5. Handler消息机制的一些原理(直接用code讲解)——Android开发

    package com.example.handlertest; import android.os.Bundle; import android.os.Handler; import android ...

  6. 【Android】Handler消息机制

    Handler消息机制主要涉及Looper.Handler.MessageQueue.Message.其中,Looper主要负责获取消息,Handler负责发送消息及处理消息,MessageQueue ...

  7. Android全面解析之由浅及深Handler消息机制

    前言 很高兴遇见你~ 欢迎阅读我的文章. 关于Handler的博客可谓是俯拾皆是,而这也是一个老生常谈的话题,可见的他非常基础,也非常重要.但很多的博客,却很少有从入门开始介绍,这在我一开始学习的时候 ...

  8. Handler消息机制与Binder IPC机制完全解析

    1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...

  9. 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制

    第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...

随机推荐

  1. Grafana+Zabbix+Prometheus 监控系统

    环境说明 软件 版本 操作系统 IP地址 Grafana 5.4.3-1 Centos7.5 192.168.18.231 Prometheus 2.6.1 Centos7.5 192.168.18. ...

  2. android 自定义view 前的基础知识

    本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...

  3. 2015 多校赛 第三场 1002 (hdu 5317)

    Description Mr. Hdu is interested in Greatest Common Divisor (GCD). He wants to find more and more i ...

  4. E - A Trivial Problem(求满足x!的尾数恰好有m个0的所有x)

    Problem description Mr. Santa asks all the great programmers of the world to solve a trivial problem ...

  5. Django中的bug总结

    1.插入数据库的时候,少写一个字段.ps:看准数据库的字段,是不是非空,是不是外键. 2.当同一个视图中需要连续操作两个数据表时,先看好两个表的外键之间的关系,再进行操作表.比如:一个订单表order ...

  6. 【SQL】多表查询

    多表查询,即查询可以从两个或多个表中获取数据.在Oracle中,有两种类型的连接格式:ANSI SQL连接格式和Oracle特有的连接格式.Oracle建议采用符合ANSI标准的连接格式. 1.内连接 ...

  7. String类面试坑题

    1.面试坑题F:\SHJT\JavaWorkspace\JavaSE\workspace\day13ezra\src\cn\itcast\sh\classcode\BTStringLastIndexO ...

  8. C++版的LLC代码

    图像稀疏编码总结:LLC和SCSPM,文章对稀疏编码讲解非常详细. <Locality-constrained Linear Coding for Image Classification> ...

  9. ANN:DNN结构演进History—RNN

    前言: CNN在图像处理领域的极大成功源于CNN的二维递进映射结构,通过训练多层卷积核来进行特征提取函数训练,在二维图像的稀疏表达和语义关联分析方面有天生的结构优势.而涉及时序问题的逻辑序列分析-边长 ...

  10. 团体程序设计天梯赛-练习集-L1-030. 一帮一

    L1-030. 一帮一 “一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组.本题就请你编写程序帮助老师自动完成这个分配工作,即在得到全班学生的排名后, ...