_057_Android_Handler的机制原理

转自https://www.cnblogs.com/xunzhi/p/5671410.html,感谢作者的无私分享。 

首先说说Handler 使用中隐藏的坑

1、delay的时间过长,导致 activity未被回收内存泄漏以及逻辑错误

  可以将Handler携程static静态内部类,或者而降handler中引用的activity位软引用

2、new 了过多的message,导致内存泄漏,应该在处理后remove这些msg

3、Activity finish()后应该remove所有的msg和runable

各种类的含义 

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,Looper和MessageQueue就是简单的三角关系。

Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。

而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。

这样说来,多个Handler都可以共享同一Looper和MessageQueue了。

这些Handler也就运行在同一个线程里,每个线程一个Loop而 一个MessageQueue。

 ------------------------------------------------------------------------------------------------------------------------------------------------------------

 使用:

子线程网主线程中发消息,直接在主线程中handler = new Handler(),子线程中可以直接用handler.sendmessage。。。

主线程网子线程中,Looper.prepare()... handler = new Handler()... looper.loop...然后handler.sendmessage

一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱

Handler的处理过程运行在创建Handler的线程里

·      一个Looper对应一个MessageQueue

·      一个线程对应一个Looper

·      一个Looper可以对应多个Handler

·      不确定当前线程时,更新UI时尽量调用post方法

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Android的主线程循环创建

  Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法(源码2.3.3):

 

 public static final void main(String[] args) {
        // other codes...

        // 创建主线程循环
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        // other codes...

        // 进入当前线程(此时是主线程)消息循环
        Looper.loop();

        // other codes...

        thread.detach();
        // other codes...
    }

 

 

  这个main()方法里面为程序创建了主线程循环

  Looper类中的主线程创建方法prepareMainLooper()

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

今天用子线程调Toast报了一个Can't create handler inside thread that has not calledLooper.prepare()错误。


解决办法很简单:
Looper.prepare();
new handler...
Looper.loop();

Looper
 

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
       sThreadLocal.set(new Looper());  //在当前线程中创建一个Looper
    }

private Looper() {
        mQueue = new MessageQueue();  //关键在这,创建Looper都干了什么。 其实是创建了消息队列
        mRun = true;
        mThread = Thread.currentThread();
    }

一般如果不是在主线程中又开启了新线程的话,一般都会碰到这个问题。
原因是在创建新线程的时候默认情况下不会去创建新的MessageQueue。

 

 

转自https://blog.csdn.net/jzlhll123/article/details/80396252,感谢作者的无私分享。

系列之三 线程间通信-Handler

系列目录 https://blog.csdn.net/jzlhll123/article/category/7671581

备注:直接跳过了系列2,是因为Binder十分复杂,想要学习后,并总结一些可能比较难。暂时跳过,先分享一些简单的。不过从目前研究的广播机制原理,contentProvider都绕不开binder。所以会尽快给自己压力学习起来!
 
  Handler是android上最常用的线程间通信工具。handler是基于某个thread/loop(主或者次)来给外部调用者去操作的。主要的用途是跨线程调用,操作主线程更新;更重要的是让很多的操作能够排队。android源码framework中就有利用handler实现的状态机,而且应用到了wifi和蓝牙上。

  接下来讲handler的原理。 
  初始化的时候,会创建一个Looper和MessageQueue,如果是自定义的thread,就得手动的开始looper()。我们看了下HandlerThread类的源码就知道,主要就是Thread封装了一下looper.perpare()和loop()。然后loop跑起来以后,就会开启一个死循环。会一直读取MessageQueue的next()方法,如果next()里面没消息了,底层让它等待着,达到阻塞线程的目的(当然需要空闲等待)。然后当生产者(sendMessage的地方很多,这里指的就是架构设计上的对于Handler而言外部整体)sendMessage()的时候,实际就是给MessageQueue队列追加,然后顺便wake一下我们的消费者loop线程,让前面的等待继续下去。 
   
  提取出来的消息,会有dispatchMessage()做一些Runnable或者callback的判断,一般的情况会给到我们熟悉的handlerMessage()里面。这个就是应用层,我们需要实现的方法。 
   
  所以回顾来看,Handler外任意线程都往一个对列里丢数据;然后,就在一个特殊的线程里面(HandleMessage也在这个线程里),对这个队列操作,或者调整顺序(AtFront等方法)。 
   
  一般地,由于使用了非静态的(普通的)内部类或者匿名内部类对象,这种一般出现在Handler和Thread直接new的时候,持有了外部类的引用。而如果在一些延迟操作的时候,生命周期比外部长,容易产生内存泄漏。handler一般使用弱引用等来解决内存泄漏。事实上最关键的,在不需要handler继续处理的时候,需要将handler的msg和callback都移除。

扩展1 底层实现:

这个wait/wake在android上实现是native层是使用epoll + pipe管道实现的。后来android6.0使用了eventfd。如果没有特别的研究的话,我们不需要关注Handler的底层实现机制,我们需要的是一个等待唤醒的模型。为什么不直接用一些java层的等待唤醒比如object的,主要是android是一个系统,cpp代码中也有Handler机制的。所以google建立了一个从上到下的方案。
扩展2 内存泄漏:

为何内部类或者匿名new的对象会产生持有外部引用? 
内部类是java编译器,在编译的时候,默认给的构造函数携带了一个外部引用进去。这个内部类我们可以使用外部类的一些方法、变量,而加上static的时候,就提示这些方法和变量错误了;这就是内部类持有了外部类引用的证据。
扩展3 framework状态机:

./core/java/com/android/internal/util/StateMachine.java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值