android高级---->Handler的原理
andriod提供了Handler来满足线程间的通信,上次在更新UI的时候也提到过Handler的使用,关于Handler的基本使用,参见博客(android基础---->子线程更新UI).今天我们深入Handler的源码,了解一个Handler的内部执行原理。
目录导航
Handler简单说明
一、 在Handler的原理说明之前,我们列出相关的重要的类:
- Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
- Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
- MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
- Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
- Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
二、 Handler主要有两种用途:
- 合理调度安排消息和runnable对象,使它们在将来的某个点被执行。
- 将一个动作入队安排在非当前线程执行。
三、 Handler创建消息
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。
四、 Handler发送消息
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。
五、 Handler处理消息
UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。注意handler是主线程中的类。
ActivityThread的说明
一、 Android的一个apk在打开时,使用到的第一个类就是这个类。
这里我们不深入Android应用程序的启动原理了,ActivityThread是一个final类,不是一个线程类:
public final class ActivityThread
二、 ActivityThread在创建的时候同时也启动了一个线程,这个线程就是一个异步线程。
他创建出了MessageQueue,并同时进行消息的轮 询,因此当这个应用程序在运行时,这个线程是一直都在的。这个线程就是应用程序的主线程,UI的处理等都是在这个线程处理的。在AcitivityThread这个类的是有一个main方法的。我们知道java应用程序的入口就是main方法。这就是程序的入口。我们来看ActivityThread的源代码:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread();
thread.attach(false); if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
} if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
} // End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");
}
这里我们重点关注两点,这个在Handler的原理分析中会做详细的说明:
- Looper.prepareMainLooper();
- Looper.loop();
Handler的预备分析
一、 首先程序在启动的时候,会执行ActivityThread类的main方法,如上我们提到两个重要的代码: Looper.prepareMainLooper();
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在他的prepare方法中,创建了一个Looper,并把它放在了ThreadLocal中:
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));
}
二、 ActivityThread中的Looper.loop();
public static void loop() {
// 进入当前线程的消息循环
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity(); for (;;) {
// 从队列中取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
} // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 分发消息
msg.target.dispatchMessage(msg); if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} // Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
} msg.recycleUnchecked();
}
}
在msg.target.dispatchMessage(msg);方法中msg.target这是分发消息的Handler
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 首先,处理Message自己的callback,调用其run方法
handleCallback(msg);
} else {
if (mCallback != null) {
// 其次,调用Handler自留的接口对象
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
handleMessage(msg);
}
}
Handler的原理分析
我们仍旧用一个例子来加以说明Handler的原理,例子代码具体可以参见我的博客:(android基础---->子线程更新UI)。以下的分析我会帖出有关例子的重要代码:
一、 创建一个Handler,处理消息:
public static final int UPDATE_TEXT = 1;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
textView.setText("I Love you.");
break;
default:
break;
}
}
}
二、 发送消息:
// 用handler处理上述问题
public void handlerUpdate(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); // 将Message对象发送出去
}
}).start();
}
三、 首先我们要创建一个handler,具体构造方法代码如下,这里由于是new Handler():callback=null,async=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;
}
四、 接下来进行了我们消息的发送:handler.sendMessage(message),源代码如下:(中间我们省略了一些过程)
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);
}
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);
}
- 所谓发送消息就是把消息放入消息队列中的合适位置,并且把消息的target设置为本Handler对象。
五、 在上述Handler的预备分析当中,我们提到过Looper的loop方法,它负责从队列中取出消息,并且分发消息。
而且我们在Handler的dispatchMessage方法也了解到,分发的消息会由Handler的handleMessage方法处理:也就是我们创建的Handler的重写方法:
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
textView.setText("I Love you.");
break;
default:
break;
}
}
- 当队列中的消息处理的时候,也会找到当时送它来的Handler对象,
- 调用其相应的dispatchMessage()方法
- 调用其中的handleMessage()方法或者mCallback成员的handleMessage()方法来进行处理。
友情链接
android高级---->Handler的原理的更多相关文章
- android handler工作原理
android handler工作原理 作用 便于在子线程中更新主UI线程中的控件 这里涉及到了UI主线程和子线程 UI主线程 它很特别.通常我们会认为UI主线程将页面绘制完成,就结束了.但是它没有. ...
- Android HttpURLConnection的使用+Handler的原理及典型应用
1.介绍 总结:HttpURLConnection用来发送和接收数据. 2.ANR异常报错 (1)ANR(Application not response) 应用无响应, 主线程(UI线程) (2)如 ...
- android handler 调用原理
1,调度原理 andriod提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange) ...
- android——handler机制原理
在android版本4.0及之后的版本中多线程有明确的分工,子线程可以写所有耗时的代码(数据库.蓝牙.网络服务),但是绝对不能碰UI,想碰UI跟着主线程走,那么我们如何才能让主线程知道我们要对 UI进 ...
- [总]Android高级进阶之路
个人Android高级进阶之路,目前按照这个目录执行,执行完毕再做扩展!!!!! 一.View的绘制 1)setContentView()的源码分析 2)SnackBar的源码分析 3)利用decor ...
- Android的Handler机制
Handler机制的原理 Android 的 Handler 机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信.之所以需 要跨线程通信是因为在 Android 中主线程通常只负责 UI ...
- Android高级之十二讲之如何降低应用内存消耗
安卓应用的内存往往是有限的,从开始的8M到16M,24M,32M,48M,64M等逐步变大,但内存的变大是由于分辨率的提高导致,并不意味着可以随意声明使用内存,而不及时回收(即使Java有自己的垃圾回 ...
- Android 高级UI设计笔记07:RecyclerView 的详解
1. 使用RecyclerView 在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常 ...
- Android之Handler探索
Handler背景理解: Handler被最多的使用在了更新UI线程中,但是,这个方法具体是什么样的呢?我在这篇博文中先领着大家认识一下什么是handler以及它是怎么样使用在程序中,起着什么样的作用 ...
随机推荐
- linux中apt-get和yum和wget的区别
1.RedHat系列:Redhat.Centos.Fedora等 yum 2.Debian系列:Debian.Ubuntu等 apt-get wget类似迅雷
- kubeadm安装kubernetes V1.11.1 集群
之前测试了离线环境下使用二进制方法安装配置Kubernetes集群的方法,安装的过程中听说 kubeadm 安装配置集群更加方便,因此试着折腾了一下.安装过程中,也有一些坑,相对来说操作上要比二进制方 ...
- Bootstrap 标签页(Tab)插件
摘自: http://www.runoob.com/bootstrap/bootstrap-tab-plugin.html Bootstrap 标签页(Tab)插件 标签页(Tab)在 Bootstr ...
- Openstack配置文件管理的变迁之路
在管理一个Openstack集群时,如何维护配置文件无疑是其中最艰难和繁琐的任务之一.因为你不仅要面对众多的核心服务(nova,keystone,glance,cinder,etc)的配置文件,还需要 ...
- Spring ActiveMQ 整合(三): 确认机制ACK(收到消息后,应该有一个回应也就是确认答复)
https://blog.csdn.net/dly1580854879/article/details/68490197
- weex开发错误汇总
weex run serve 报UglifyJS错 ANDROID_HOME环境变量 weex build android需要ANDROID_HOME, 请配置 D:\adt-windows-x86_ ...
- Kafka的安装和设置
Kafka是一种分布式发布订阅消息系统. Kafka有三种模式: (1)单节点单Broker,在一台机器上运行一个Kafka实例: (2)单节点多Broker,在一台机器上运行多个Kafka实例: ( ...
- asp.net core2->2.1 webapi 进行了重大变更
传统的在 启动时候 使用Mvc路由的配置不再有效.而是基于Attribute的声明标注进行配置路由.
- IDEA使用笔记(八)——自动生成 serialVersionUID 的设置
这个设置比较简单,也有一些博文已经写到了,为什么我还要写哪?(潜台词:因为我想凑一篇博文)我觉得学习,特别是编程学习是需要重复造轮子的,另外,就是加深自己的印象方便自己的查找.还有就是关键点,有些博客 ...
- 判断Android 当前版本是否为debug版本
public static boolean isDebugVersion(Context context) { try { ApplicationInfo info = context.getAppl ...