Android之异步消息处理机制Handler源码解析
转载请标明出处:
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的使用方法
- 通过post(runnable)方法进行延迟消息的处理。
- 通过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使用方法:
- 通过post(runnable)方法进行延迟消息的处理。
- 通过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源码解析相关推荐
- Handler全家桶之 —— Handler 源码解析
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...
- Handler 源码解析——Handler的创建
前言 Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信.这是两个完全不同的概念. Handler先进先出原则,Looper类用来管理特定 ...
- Handler源码解析2
Handler源码解析1 https://blog.csdn.net/qq_44076155/article/details/110676740 Handler源码解析2 https://blog.c ...
- Android技术栈--HashMap和ArrayMap源码解析
1 总览 WARNING!!:本文字数较多,内容较为完整并且部分内容难度较大,阅读本文需要较长时间,建议读者分段并耐心阅读. 本文会对 Android 中常用的数据结构进行源码解析,包括 HashMa ...
- JVM类加载机制(ClassLoader)源码解析
http://blog.csdn.net/chenyi8888/article/details/7066569 其实JVM类加载机制,简单地说就是类管理,也就是我们生成的class文件. 三个步骤:装 ...
- Spring事件机制Event源码解析(未完待续)
Spring事件机制Event源码解析(未完待续) 监听器: ApplicationEvent事件 ApplicationListener监听器(观察者) ApplicationEventMultic ...
- Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...
- Dubbo第三讲:Dubbo的可扩展机制SPI源码解析
本文是Dubbo第三讲:Dubbo的可扩展机制SPI源码解析 文章目录 1.Dubbo SPI机制 1.1.Dubbo具有良好拓展性的原因 1.2.Dubbo SPI和Java SPI的区别? 1.3 ...
- Android多线程----异步消息处理机制之Handler
虽然是国庆佳节,但也不能停止学习的脚步,我选择在教研室为祖国母亲默默地庆生. 关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在 ...
最新文章
- Android Studio中新建和引用assets文件
- java 升级1.8_升级java到1.8.0_111
- 理想的教育是从父母自我改变开始
- real210开发板tslib1.4移植
- 【lucene】lucene自定义 filter
- 推荐系统组队学习——WideDeep
- 李航教授展望自然语言对话领域:现状与未来
- SVM之-二分类延伸到多分类
- 如何设置.net控件SplitContainer平均分配
- 基于FBX SDK的FBX模型解析与加载 -(二)
- 【测试沉思录】17. 性能测试中的系统资源分析之四:网络
- 盘点国内外那些形式多样的能量采集技术
- 使用docker在Centos上做DNS服务器的配置
- ARM汇编指令—CPSR访问指令(mrsmsr)
- 大学物理复习--磁场中的磁介质
- idea merge into current 是啥意思?
- 易语言支持库制作学习笔记
- NGINX配合FASTDFS使用的安装与配置及编译遇到错误:cc1: all warnings being treated as errors
- hcip结课实验ensp,思考改进点以及遇到的问题
- 如何把绘制好的Excel表格插入到CAD中呢?