版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ly502541243/article/details/52062179
转载请注明出处http://blog.csdn.net/ly502541243/article/details/52062179
Handler
每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然。今天看到Handler.post这个方法之后决定再去翻翻源代码梳理一下Handler的实现机制。

异步更新UI
先来一个必背口诀“主线程不做耗时操作,子线程不更新UI”,这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出现在我们面前了(AsyncTask也行,不过本质上还是对Handler的封装),来一段经典常用代码(这里忽略内存泄露问题,我们后面再说):

首先在Activity中新建一个handler:

private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0:mTestTV.setText("This is handleMessage");//更新UIbreak;}}};

然后在子线程里发送消息:

new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络mHandler.sendEmptyMessage(0);} catch (InterruptedException e) {e.printStackTrace();}}}).start();

至此完成了在子线程的耗时操作完成后在主线程异步更新UI,可是并没有用上标题的post,我们再来看post的版本:

private Handler mHandler;//全局变量
@Override
protected void onCreate(Bundle savedInstanceState) {mHandler = new Handler();new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络mHandler.post(new Runnable() {@Overridepublic void run() {mTestTV.setText("This is post");//更新UI}});} catch (InterruptedException e) {e.printStackTrace();}}}).start();
}

从表面上来看,给post方法传了个Runnable,像是开了个子线程,可是在子线程里并不能更新UI啊,那么问题来了,这是怎么个情况呢?带着这个疑惑,来翻翻Handler的源码:

先来看看普通的sendEmptyMessage是什么样子:

public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}

将我们传入的参数封装成了一个消息,然后调用sendMessageDelayed:

public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

再调用sendMessageAtTime:

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

好了,我们再来看post():

public final boolean post(Runnable r){return  sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是两种发送消息的不同之处}

方法只有一句,内部实现和普通的sendMessage是一样的,但是只有一点不同,那就是 getPostMessage® 这个方法:

private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}

这个方法我们发现也是将我们传入的参数封装成了一个消息,只是这次是m.callback = r,刚才是msg.what=what,至于Message的这些属性就不看了

Android消息机制
看到这里,我们只是知道了post和sendMessage原理都是封装成Message,但是还是不清楚Handler的整个机制是什么样子,继续探究下去。

刚才看到那两个方法到最终都调用了sendMessageAtTime

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,看名字应该是把消息加入队列的意思,点进去看下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

mAsynchronous这个异步有关的先不管,继续将参数传给了queue的enqueueMessage方法,至于那个msg的target的赋值我们后面再看,现在继续进入MessageQueue类的enqueueMessage方法,方法较长,我们看看关键的几行:

Message prev;
for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;

果然像方法名说的一样,一个无限循环将消息加入到消息队列中(链表的形式),但是有放就有拿,这个消息怎样把它取出来呢?

翻看MessageQueue的方法,我们找到了next(),代码太长,不赘述,我们知道它是用来把消息取出来的就行了。不过这个方法是在什么地方调用的呢,不是在Handler中,我们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息,关键代码如下:

for (;;) {Message msg = queue.next(); // might block...msg.target.dispatchMessage(msg);...msg.recycleUnchecked();

简单明了,我们看到了我们刚才说的msg.target,刚才在Handler中赋值了msg.target=this,所以我们来看Handler中的dispatchMessage:

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

msg的callback不为空,调用handleCallback方法(message.callback.run())
mCallback不为空,调用mCallback.handleMessage(msg)
最后如果其他都为空,执行Handler自身的 handleMessage(msg) 方法
msg的callback应该已经想到是什么了,就是我们通过Handler.post(Runnable r)传入的Runnable的run方法,这里就要提提java基础了,直接调用线程的run方法相当于是在一个普通的类调用方法,还是在当前线程执行,并不会开启新的线程。

所以到了这里,我们解决了开始的疑惑,为什么在post中传了个Runnable还是在主线程中可以更新UI。

继续看如果msg.callback为空的情况下的mCallback,这个要看看构造方法:

1.
public Handler() {this(null, false);}
2.
public Handler(Callback callback) {this(callback, false);}
3.
public Handler(Looper looper) {this(looper, null, false);}
4.
public Handler(Looper looper, Callback callback) {this(looper, callback, false);}
5.
public Handler(boolean async) {this(null, async);}
6.
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;}
7.
public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}

具体的实现就只有最后两个,已经知道mCallback是怎么来的了,在构造方法中传入就行。

最后如果这两个回调都为空的话就执行Handler自身的handleMessage(msg)方法,也就是我们熟知的新建Handler重写的那个handleMessage方法。

Looper
看到了这里有一个疑惑,那就是我们在新建Handler的时候并没有传入任何参数,也没有哪里显示调用了Looper有关方法,那Looper的创建以及方法调用在哪里呢?其实这些东西Android本身已经帮我们做了,在程序入口ActivityThread的main方法里面我们可以找到:

 public static void main(String[] args) {...Looper.prepareMainLooper();...Looper.loop();...

总结
已经大概梳理了一下Handler的消息机制,以及post方法和我们常用的sendMessage方法的区别。来总结一下,主要涉及四个类Handler、Message、MessageQueue、Looper:

新建Handler,通过sendMessage或者post发送消息,Handler调用sendMessageAtTime将Message交给MessageQueue

MessageQueue.enqueueMessage方法将Message以链表的形式放入队列中

Looper的loop方法循环调用MessageQueue.next()取出消息,并且调用Handler的dispatchMessage来处理消息

在dispatchMessage中,分别判断msg.callback、mCallback也就是post方法或者构造方法传入的不为空就执行他们的回调,如果都为空就执行我们最常用重写的handleMessage。

最后谈谈handler的内存泄露问题
再来看看我们的新建Handler的代码:

private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {...}};

当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有Activity的引用。

而Handler通常会伴随着一个耗时的后台线程一起出现,这个后台线程在任务执行完毕后发送消息去更新UI。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束。

另外,如果执行了Handler的postDelayed()方法,那么在设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

解决方法之一,使用弱引用:

static class MyHandler extends Handler {WeakReference<Activity > mActivityReference;MyHandler(Activity activity) {mActivityReference= new WeakReference<Activity>(activity);}@Overridepublic void handleMessage(Message msg) {final Activity activity = mActivityReference.get();if (activity != null) {mImageView.setImageBitmap(mBitmap);}}
}

————————————————
版权声明:本文为CSDN博主「Silly_Monkey」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ly502541243/article/details/52062179

Android Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)相关推荐

  1. 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)

    http://blog.csdn.net/ly502541243/article/details/52062179/

  2. android 结束if循环_Android 消息机制(Handler + MessageQueue + Looper)

    Author:CrazyWah Date:2018.03.26 CopyRight:http://crazywah.com 禁止搬运!!!禁止搬运!!!禁止搬运!!! Android的消息机制主要由H ...

  3. Android中的消息机制:Handler消息传递机制

    参考<疯狂android讲义>第2版3.5 P214 一.背景 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为 ...

  4. Android 消息机制(Handler运行机制)

     1 Android 消息机制 Android 的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑 2 为什么要用Handler消息 ...

  5. Android消息机制(Handler、MessageQueue、Looper)详细介绍

    Android的消息机制其实在android的开发过程中指的也就是Handler的运行机制,这也就引出了android中常见的面试问题: 简述Handler.Looper.MessageQueue的含 ...

  6. 小米最新系统android 10,小米MIUI 12再更新,基于Android 10的系统也来了,5项改变...

    原标题:小米MIUI 12再更新,基于Android 10的系统也来了,5项改变 小米MIUI官方刚刚发布了MIUI 20.6.17,目前MIUI 20.6.18也来了,这次更新,带来了一个很好的消息 ...

  7. android 一个字符串分两行显示_重新梳理Android权限管理

    Android Developer指南中,对Android安全体系结构的核心有这么一个说法:默认情况下,任何应用程序都无权执行任何会对其他应用程序.操作系统或者用户产生负面影响的操作.这句话其实就很好 ...

  8. Android Handler处理机制 ( 三 ) ——Handler,Message,Looper,MessageQueue

    在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知 handler基本使用: 在主线程中,使用handler很简单,new一个Handle ...

  9. Android的消息机制: Message/MessageQueue/Handler/Looper

    概览 * Message:消息.消息里面可包含简单数据.Object和Bundle,还可以包含一个Runnable(实际上可看做回调). * MessageQueue:消息队列,供Looper线程消费 ...

  10. 【Android】线程间通信——Handler消息机制

    文章目录 引言 Java层 永动机跑起来 示例 Looper Handler MessageQueue 永动机停下 Native层 nativeInit() nativePollOnce() nati ...

最新文章

  1. Python编程入门到实践 - 笔记( 4 章)
  2. 【Linux系统编程】Linux文件操作
  3. (翻译)Google Guava Cache
  4. C++11并发实战(专栏)
  5. 理想更新“货车并线预警”遭用户吐槽 李想:仍在优化
  6. C++ 返回类型协变
  7. python与r语言哪个好学_python和r哪个难一点
  8. 用C/C++实现SMC动态代码加密技术
  9. 操作系统实验ucore lab1
  10. 您的服务器组件没有得到合法授权,服务器将会受限模式运行
  11. 【千锤百炼Python—14】:修改图片格式
  12. keil5 调试不进主函数一直卡在0x1FFFF3B2 F8D01808 LDR r1,[r0,#0x808]
  13. Codeforces 831 A Unimodal Array
  14. 有关51单片机串口通信点灯的问题
  15. 【抓包】【Mac Charles】局域网无法抓包 手机热点来解决
  16. 在Windows 7镜像中整合“SP2”补丁
  17. spark基础理论及优化思路
  18. 【Linux】之systemd与systemctl
  19. 惊了 消息中间件合集:MQ(ActiveMQ/RabbitMQ/RocketMQ)+Kafka+笔记
  20. 利用$ajax实现远程登录判断

热门文章

  1. ARM处理器的9种模式详解
  2. thinkphp建站-前后台文件配置
  3. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
  4. Android应用系列:双击返回键退出程序
  5. Android解决异常apk on device '0292bea1': Unable to open sync connection!
  6. AE+C#实现:在SceneControl里打开和保存
  7. 预训练模型:一种低资源实体NER标注的方法
  8. 位置编码在注意机制中的作用
  9. 分享两本高质量算法书籍
  10. 真实场景下如何解决类别不平衡问题