转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/76083113
本文出自:【顾林海的博客】

个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!

前言

我们知道在应用启动时会开启一个主线程,也就是UI线程,主线程主要管理与用户交互的UI控件(UI展示,事件分发),如果在主线程中执行耗时操作会触发ANR(Application not responding)异常,意思是程序未响应,这时就需要开启子线程来执行耗时操作,这时又会引发到子线程更新UI的问题,这里要记住Android中主线程是不安全的,为了解决子线程能更新UI状态,因此在Android平台中引入了一个叫做Handler机制,可以通过在子线程通过Handler发送消息给主线程来进行UI的更新,并且在子线程中可以进行耗时操作。总的来说Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

Handler的使用方法

  1. 通过post(runnable)方法进行延迟消息的处理。
  2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。

    首先我们演示第一种方式,通过post(runnable)方法来进行消息的处理,具体代码如下:

package com.glh.intentservice;import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private TextView mInfoTextView;private Button mDownloadButton;private static Handler mHandler = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();initEvent();}private void initView() {mInfoTextView = (TextView) findViewById(R.id.tv_info);mDownloadButton = (Button) findViewById(R.id.btn_download);}private void initEvent() {mDownloadButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {mInfoTextView.setText("下载中...");new DownloadThread(0).start();}});}class DownloadThread extends Thread {private int mProcess = 0;DownloadThread(int process) {mProcess = process;}@Overridepublic void run() {while (mProcess < 100) {mProcess++;}try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}Runnable runnable = new Runnable() {@Overridepublic void run() {mInfoTextView.setText("下载成功");}};mHandler.post(runnable);}}}

分析上面代码,我们在创建Handler时,是在主线程中创建的,也就是说这个Handler绑定在主线程中,代码中,创建继承自Thread类的DownloadThread的线程类,并在run方法中执行了一个耗时操作,这里可以看出执行完耗时操作后,创建了一个Runnable对象,并在Runnable中的run方法中进行UI的更新,最后通过Handler的post方法进行消息的处理。

最后看看sendMessage(message)方法使用,代码如下:

package com.glh.intentservice;import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;import java.lang.ref.WeakReference;public class MainActivity extends AppCompatActivity {private TextView mInfoTextView;private Button mDownloadButton;private MyHandler mHandler = new MyHandler(this);static class MyHandler extends Handler {private WeakReference<MainActivity> weakReference;MyHandler(MainActivity activity) {weakReference = new WeakReference<>(activity);}@Overridepublic void handleMessage(Message msg) {MainActivity activity = weakReference.get();if (null != activity) {int what = msg.what;switch (what) {case 1:activity.downloadSuccess((String) msg.obj);break;default:break;}}}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();initEvent();}private void initView() {mInfoTextView = (TextView) findViewById(R.id.tv_info);mDownloadButton = (Button) findViewById(R.id.btn_download);}private void initEvent() {mDownloadButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {mInfoTextView.setText("下载中...");new DownloadThread(0).start();}});}private void downloadSuccess(String info) {mInfoTextView.setText(info);}class DownloadThread extends Thread {private int mProcess = 0;DownloadThread(int process) {mProcess = process;}@Overridepublic void run() {while (mProcess < 100) {mProcess++;}try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}Message message = Message.obtain();message.what = 1;message.obj = "下载成功";mHandler.sendMessage(message);}}}

> 在代码中创建一个静态内部类MyHandler,为什么要创建静态内部类呢?这是因为,非静态内部类会持有外部类Activity的匿名引用,如果在子线程执行耗时操作,在耗时操作时,Activity退出,通过Handler发送消息,这时由于Activity不存在会引起Activity的内存泄露,因此这里使用静态内部类,并使用WeakReference弱引用。 > 代码中创建了一个线程,并执行了耗时操作,耗时操作完毕后,创建了Message对象,并给Message的what设置1,obj设置"下载成功",这里的what属性用于在主线程中Handler的handleMessage方法中进行不同Message的处理,obj属性用于携带相关数据。最后通过Handler的sendMessage(message)方法发送消息给主线程,主线程的Handler的handleMessage方法中通过msg.what识别哪个message,并进行相应的处理。

Handler机制

在讲解Handler机制时,先来了解一下Looper、MessageQueue、Message所代表的含义。Handler主要包含两种作用,分别是发送消息到消息队列和处理消息;Looper用于为当前线程生成一个消息队列,并执行一个循环,不停的从消息队列中获取Message消息;MessageQueue就是一个先进先出的消息队列,提供了next方法用于获取Message消息,以及enqueueMessage方法将消息添加到消息队列中;Message代表消息。

在Android中新开启的线程是没有开启消息循环的(主线程除外),如果在子线程中开启消息循环就需要这样做:

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();}
}

在run方法中主要三件事:

  • 通过Looper.prepare实例化Looper对象,并生成一个消息队列。
  • 创建Handler,并与Looper相关联。
  • 通过Looper.loop开启消息循环,并处理消息。

    先查看Looper,prepare()方法:

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));
}

在prepare方法中sThreadLocal是ThreadLocal对象,在创建Handler时通过sThreadLocal获取当前线程的Looper,这个在后面会提到,上面代码中会先判断sThreadLocal是否为null,如果不为null,就抛出异常,说明Looper.prepare()方法不能执行两次以上,也就是说一个线程中只有一个Looper实例,接着看Looper的构造器。

private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}

在Looper构造器中创建了MessageQueue对象,mThread是指当前的线程。到这里Looper.prepare()方法执行完毕,总结Looper.prepare方法做了如下工作:

  • 一个线程只能有一个Looper实例。
  • 创建Looper时,会关联一个MessageQueue。

    接着看Looper.loop()方法:

public static void loop() {final Looper me = myLooper();final MessageQueue queue = me.mQueue;for (; ; ) {//没有消息时阻塞Message msg = queue.next(); // might blockif (msg == null) {return;}try {//通过Handler方法消息msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}//消息的回收msg.recycleUnchecked();}
}

上面的代码进行了局部精简,在loop()方法中,先是通过myLooper()方法获取当前线程的Looper对象,在myLooper()方法中通过sThreadLocal.get()方法获取Looper对象,这里的sThreadLocal在讲解Looper.prepare()方法时已经讲过了。接着获取当前Looper的MessageQueue消息队列,并通过一个无限循环遍历消息队列,当消息队列中没有消息时处于阻塞状态,当消息队列中有消息时,取出消息,通过msg.target的dispatchMessage方法发送消息,这里的msg.target就是Handler,最后进行消息的回收。总结Looper.loop()方法做了如下工作:

  • 获取当前线程Looper对象。
  • 从当前线程中的Looper对象中获取消息队列。
  • 开启无限循环,遍历消息队列。
  • 在循环中通过msg.target进行消息的发送。

最后看看Handler的源码:

public Handler() {this(null, false);
}public Handler(Callback callback, boolean async) {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;mAsynchronous = async;
}

mLooper是之前在Loop.prepare()方法执行时关联的Looper对象,这这段代码中会去判断mLooper是否空,如果为空抛出异常,说明创建Handler之前需要通过Looper.prepare()方法创建当前线程的Looper对象并创建一个MessageQueue消息队列,这样的话Handler才能与MessageQueue进行关联,这里面mQueue就是Looper创建的MessageQueue对象。

Handler创建完毕后就需要我们进行发送消息,这里主要分析前面两种Handler使用方法:

  1. 通过post(runnable)方法进行延迟消息的处理。
  2. 通过sendMessage(message)方法进行子线程与主线程之间的消息传递。
public final boolean post(Runnable r)
{return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;
}

通过post(runnable)方法进行消息的处理时,Handler内部会调用sendMessageDelayed方法,而sendMessageDelayed方法的第一个参数就是Message对象,通过getPostMessage(runnable)方法进行包装,在getPostMessage方法中获取Message对象,并将Runnable对象赋值给Message的callback属性。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

一直往下追溯,最终调用的是enqueueMessage方法,在enqueueMessage方法中,将当前的Handler赋值给Message的target属性,这里面的msg.target是不是很熟悉,在之前Looper.loop()方法中进行消息队列的遍历,最终就是通过它来发送消息的。这里的queue就是与当前线程关联的Looper对象的MessageQueue,执行到最后通过MessageQueue的enqueueMessage方法将Message放入消息队列中。

接着查看sendMessage(message)方法的实现原理:

public final boolean sendMessage(Message msg)
{return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

查看到这里,大家就会察觉到,这里与上面通过post(runnable)方法的实现逻辑是一样,都是通过MessageQueue的enqueueMessage方法将Message放入消息队列中。当消息放入消息队列MessageQueue后,是如何获取Message并进行处理的呢?这里又回到了上面Looper.loop()方法中的无限循环遍历消息队列中的msg.target的dispatchMessage方法,从上面存放消息队列的分析中,我们已经知道了msg.target就是一个Handler,因此,我们查看Handler中的dispatchMessage方法:

public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

这里面的callback就是Message中的Runnable对象,我们在上面讲解Handler的使用方法中讲的第一种方法,通过post(runnable)方法,这里面的runnable在post(runnable)源码中封装Message时,已经赋值给了Message的callback属性,回到上面的代码中,第一步判断Message的callback属性是否为空,不为空执行handleCallback(msg)方法,如果为空,判断当前Handler的mCallback属性是否为空,如果不为空,执行Callback 的handleMessage的方法,如果Handler的mCallback属性为空或者是Callback的handleMessage方法返回false,执行Handler中的方法handleMessage(msg),这里面的mCallback是一个内部Callback接口:

public interface Callback {public boolean handleMessage(Message msg);
}

handleMessage方法是不是很熟悉,没错,它是在创建Handler时,如果实现了handleMessage方法,也就是说交由Handler来处理消息时,会执行这个方法。如果只是new Handler(),那么这个mCallback为空。

如果通过post(runnable)方法处理消息,这时会执行handleCallback(msg)方法:

private static void handleCallback(Message message) {message.callback.run();
}

很简单,就是调用Runnable的run()方法,准确来说,post(runnable)并没有开启线程,只是纯粹的调用Runnable的run方法,这也说明了为什么通过Handler可以更新UI。

到此Handler机制原理解析完毕。

Android之异步消息处理机制Handler源码解析相关推荐

  1. Handler全家桶之 —— Handler 源码解析

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...

  2. Handler 源码解析——Handler的创建

    前言 Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信.这是两个完全不同的概念. Handler先进先出原则,Looper类用来管理特定 ...

  3. Handler源码解析2

    Handler源码解析1 https://blog.csdn.net/qq_44076155/article/details/110676740 Handler源码解析2 https://blog.c ...

  4. Android技术栈--HashMap和ArrayMap源码解析

    1 总览 WARNING!!:本文字数较多,内容较为完整并且部分内容难度较大,阅读本文需要较长时间,建议读者分段并耐心阅读. 本文会对 Android 中常用的数据结构进行源码解析,包括 HashMa ...

  5. JVM类加载机制(ClassLoader)源码解析

    http://blog.csdn.net/chenyi8888/article/details/7066569 其实JVM类加载机制,简单地说就是类管理,也就是我们生成的class文件. 三个步骤:装 ...

  6. Spring事件机制Event源码解析(未完待续)

    Spring事件机制Event源码解析(未完待续) 监听器: ApplicationEvent事件 ApplicationListener监听器(观察者) ApplicationEventMultic ...

  7. Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  8. Dubbo第三讲:Dubbo的可扩展机制SPI源码解析

    本文是Dubbo第三讲:Dubbo的可扩展机制SPI源码解析 文章目录 1.Dubbo SPI机制 1.1.Dubbo具有良好拓展性的原因 1.2.Dubbo SPI和Java SPI的区别? 1.3 ...

  9. Android多线程----异步消息处理机制之Handler

    虽然是国庆佳节,但也不能停止学习的脚步,我选择在教研室为祖国母亲默默地庆生. 关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在 ...

最新文章

  1. Android Studio中新建和引用assets文件
  2. java 升级1.8_升级java到1.8.0_111
  3. 理想的教育是从父母自我改变开始
  4. real210开发板tslib1.4移植
  5. 【lucene】lucene自定义 filter
  6. 推荐系统组队学习——WideDeep
  7. 李航教授展望自然语言对话领域:现状与未来
  8. SVM之-二分类延伸到多分类
  9. 如何设置.net控件SplitContainer平均分配
  10. 基于FBX SDK的FBX模型解析与加载 -(二)
  11. 【测试沉思录】17. 性能测试中的系统资源分析之四:网络
  12. 盘点国内外那些形式多样的能量采集技术
  13. 使用docker在Centos上做DNS服务器的配置
  14. ARM汇编指令—CPSR访问指令(mrsmsr)
  15. 大学物理复习--磁场中的磁介质
  16. idea merge into current 是啥意思?
  17. 易语言支持库制作学习笔记
  18. NGINX配合FASTDFS使用的安装与配置及编译遇到错误:cc1: all warnings being treated as errors
  19. hcip结课实验ensp,思考改进点以及遇到的问题
  20. 如何把绘制好的Excel表格插入到CAD中呢?

热门文章

  1. linux命令行tcp连接,linux下2个检查tcp连接的命令
  2. oracle雾化试图_Oracle创建物化视图
  3. windows端自动化遇到的问题
  4. Redis重新连接弹性
  5. 软件体系架构阅读笔记一
  6. JS基础-Array对象手册
  7. 【转】Android菜单详解——理解android中的Menu--不错
  8. 关于Shiro框架权限标识符中*使用的总结
  9. 在温暖的南方惠州①月了。。
  10. CListCtrl使用技巧