Android---Handler 源码解析

在 android 开发中,经常会在子线程中进行一些操作,当操作完后会通过 handler 发送一些数据给主线程,通知主线程做响应的操作。原理:子线程 handler 主线程,其实构成了线程模型中的经典问题,生产者-消费者模型(子线程往消息队列中放消息,主线程从消息队列中取消息)。生产者-消费者模型:生产者和消费者在同一时间段内共用一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据。

1 Handler 源码分析

1.1 handler 机制的相关类

\bullet Handler: 发送和接收消息;

\bullet Looper: 用于轮询消息队列,一个线程只能有一个Looper;

\bullet Message: 消息实体;

\bullet MessageQueue: 消息队列,用于存储消息和管理消息。

1.2 创建 Looper

创建 Looper 的方法是调用 Looper.prepare() 方法。在 ActivtyThread 中的 main 方法为我们 prepare 了

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    //其他代码省略...
    Looper.prepareMainLooper(); //初始化Looper以及MessageQueue

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

Looper.prepareMainLopper()

public static void prepareMainLooper() {
    prepare(false);//调用prepare(), 消息队列不可以quit
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been
            prepared.");
        }
        sMainLooper = myLooper();
    }
}

prepare 有两个重载的方法,主要看 prepare(boolean quitAllowed),quitAllowed 的作用是创建 MessageQueue 时标识消息队列是否可以销毁,主线程不可以销毁。

public static void prepare() { 
    prepare(true);//消息队列可以quit
  }
  // 私有的构造函数
  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//不为空表示当前线程已经创建了Looper
            throw new RuntimeException("Only one Looper may be created per thread");
            //每个线程只能创建一个Looper
        }
        // ThreadLocal 多线程中重要的知识点,线程上下文的存储变量
        sThreadLocal.set(new Looper(quitAllowed));//创建Looper并设置给sThreadLocal,这样get的
        时候就不会为null了
  }

注意:一个线程只要一个Looper。如何保证只有一个?

       只有一个ThreadLocal(源码里为final),就保证只有一个 Looper。ThreadLocalMap里的<key, value>  ---->    <唯一的ThreadLocal, Looper>。为了保证 ThreadLocalMap.set(value) 时,value 不会被覆盖(即Looper不会改变),会先进行上面代码的 if 操作,if (sThreadLocal.get() != null) , 不为空表示当前线程已经创建了Looper,然后直接抛异常结束prepare。如果 if 不成立,则 new Looper()。

1.3 创建MessageQueue以及Looper与当前线程的绑定

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
    mThread = Thread.currentThread(); //当前线程的绑定
}
MessageQueue 的构造方法
MessageQueue(boolean quitAllowed) {
    //mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的            quit()方法
    //就不贴源码了
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

1.4 Looper.loop() 

同时是在 main 方法中 Looper.prepareMainLooper() 后 Looper.loop() 开始轮询

public static void loop() {
    final Looper me = myLooper();//里面调用了sThreadLocal.get()获得刚才创建的Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this
        thread.");
    }//如果Looper为空则会抛出异常
    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) {

            //由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage
            enqueueMessage后
            //队列里才有消息
            // 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);//msg.target就是绑定的Handler,详见后面Message的部
    分,Handler开始
    //后面代码省略.....
    msg.recycleUnchecked();
    }
}

1.5 创建 Handler

最常见的创建 handler 的方式

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
    super.handleMessage(msg);
    }
};

在内部调用 this(null, false);

public Handler(Callback callback, boolean async) {
    //前面省略
    mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**!
    if (mLooper == null) {
        throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//创建消息队列MessageQueue
    mCallback = callback; //初始化了回调接口
    mAsynchronous = async;
}
Looper.myLooper()
//这是Handler中定义的ThreadLocal ThreadLocal主要解多线程并发的问题
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
sThreadLocal.get() will return null unless you’ve called prepare(). 这句话告诉我们 get 可能返回 null 除非先调用 prepare()方法创建 Looper 。在前面已经介绍了

1.6 创建 Message

可以直接 new Message()  但是有更好的方式 Message.obtain()。因为可以检测是否有可以复用的 Message,复用避免过多的创建、销毁 Message 对象达到优化内存和性能的目的。
public static Message obtain(Handler h) {
    Message m = obtain();//调用重载的obtain方法
    m.target = h;//并绑定的创建Message对象的handler

    return m;
}
public static Message obtain() {
    synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
    if (sPool != null) {
        //sPool是就是handler dispatchMessage 后 通过recycleUnchecked回收用以复用的Message
        Message m = sPool;
        sPool = m.next;
        m.next = null;
        m.flags = 0; // clear in-use flag
        sPoolSize--;
        return m;
        }
    }
    return new Message();
}

1.7 Message 和 Handler 的绑定

创建 Message 的时候可以通过 Message.obtain(Handler h) 这个构造方法绑定。当然也可以在 Handler 中的 enqueueMessage() 绑定,所以发送 Message 的方法都会调用此方法队列,所以在创建 Message 的时候是可以不绑定的。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //Message 与 Handler 绑定
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }

    return queue.enqueueMessage(msg, uptimeMillis);
}

1.8 Handler 发送消息

Handler 发送消息的重载方法很多,但是主要的只有2种。sendMessage(Message), sendMessage方法通过一些列重载方法调用,sendMessage --(调用)--> sendMessageDelayed --(调用)--> sendMessageAtTime  --(调用)--> enqueueMessage(MessageQueue里),将消息保存在消息队列中,最终由 Looper 取出,交给 Handler 的 dispatchMessage 进行处理。

我们可以看到在 dispatchMessage 方法中,message 中 callback 是一个 Runable 对象,如果 msg.callback != null,则直接调用 callback 的 run 方法,否则判断 mCallBack 是否为空,mCallback 在 Handler 构造方法初始化,在主线程直接通过无参的构造方法 new 出来的为 null,所以会直接执行后面的 handleMessage() 方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//callback在message的构造方法中初始化或者使用
handler.post(Runnable)时候才不为空
    handleCallback(msg);
    } else {
        if (mCallback != null) {//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,
该属性为null,此段不执行
            if (mCallback.handleMessage(msg)) {
            return;
            }
        }
    handleMessage(msg);//最终执行handleMessage方法
    }
}
    private static void handleCallback(Message message) {
    message.callback.run();
}

1.9 Handler 处理消息

在 handleMessage(Message) 方法中,我们可以拿到 message 对象,根据不同的需求进行处理,整个 Handler 机制的流程就结束了。

handler不仅仅是用于线程间通信,它只是 handler 的一个附属功能。而 handler 正真的功能是"所以代码都是在 handler 中运行,它维持着我们 Android app 运行的整个框架"。

Handler 线程间通信的整个调度流程

handler.sendMessage(开始) -->  MessageQueue.enqueueMessage(消息队列,把Message 插入消息队列) --> looper.loop() --> messageQueue.next() --> handler.dispatchMessage--> handler.handleMessage(结束)

子线程 到 主线程的切换:handler.sendEmptyMessage(子线程) -到-> handler.handleMessage(主线程)

MessageQueue: 由单链表实现的优先级队列(按时间排序)

MessageQueue 里有一个 message 对象,一个 next(Message)也是 Message 对象。

MessageQueue(fianl) 是跟随 Looper 创建的

Handler 相关面试题:

1  一个线程有几个 Handler ?

Hanler 机制只有一个,Handler 想要几个就几个(new Handler) 

2 一个线程有几个 Looper ?如何保证?

略....

3 Handler 内存泄漏原因?为什么其它的内部类没有说过这个问题?

java基础:内部类会持有外部类对象(Handler 是一个内部类)。例如 RecyclerView 的 Adapter 里的 ViewHolder 也是一个内部类,它就没有内存泄漏的问题

答:与生命周期有关。

4 子线程里 new Handler 要做什么准备工作?

在子线程里准备 Looper.prepare() 和 Looper.loop()两个工作,而主线程是在程序启动时,ActivtyThread 里的 main 方法就为我们准备了。

子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

调用 quit()。调用 quit 首先会唤醒线程,然后 MessageQueue 给了一个 null,最后退出 loop (结束了)

既然可以存在多个 Handler MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?取消息呢?

锁。

synchronized: 内置锁。可以修饰代码块、函数。上锁、开锁都是由 JVM 完成(内置到系统里面的锁 --> 内置锁)。

7 Looper 死循环为什么不会导致应用卡死?

ANR(Application Not Response)应用不响应。Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR

ThreadLocal 全局唯一(整个App 就一个, 由 static final 修饰),Looper 是线程唯一(一个线程一个)

线程1 --》ThreadLocalMap 1 --》<唯一的ThreadLocal, Looper1> --》MessageQueue1

线程2 --》ThreadLocalMap 2 --》<唯一的ThreadLocal, Looper2> --》MessageQueue2

享元设计模式

用于减少创建对象的数量,以减少内存占用和提高性能。可以防止内存抖动,内存复用。

Handler中message的内容置空、Activity的管理都是享元设计模式

消息机制之同步屏障 -- 架构思维

同步屏障:处理紧急的消息。

消息是根据执行时间进行先后排序,然后消息是保存在队列中,因而消息只能从队列的队头取出来。当有紧急消息时,就会将 msg.target 置为 null,当我们在轮询消息时,发现当前 msg != null && msg.target == null(如下代码), 就会遍历整个 msg 队列,找到那个紧急消息,然后处理掉。

HandlerThread 存在的意义

HandlerThread 是 Thread 的子类,就是一个线程,只是它在自己的线程里帮我们创建了 Looper

\bullet 方便使用,方便初始化,方便获取线程 looper

\bullet 保证了线程安全

HandlerThread 的用法:在子线程中创建一个自己的 Looper

 

上面代码为 HandelrThread 里的 run 方法,为我们创建一个自己的 Looper,因为它里面有写 Looper.prepare() 和 mLooper = Looper.myLooper(); 然后通过 getLooper() 方法就能获取到 HandlerThread 为我们创建的 Looper。

当 run() 方法执行的 Looper.prepare() 时,此时右调用了 getLooper() 方法,那么 mLooper 就为空,此时执行 wait() 操作,并释放锁。wait 释放锁后 run() 方法继续运行,给 mLooper 赋值为 Looper.myLooper()。然后执行 notifyAll() 唤醒其它等待的锁(是唤醒,并不代表这里的 getLooper 右继续运行),notifyAll() 并不能释放锁。当 run() 方法全部执行完后,getLooper() 方法里才又重新拿到锁继续运行。注意:run() 方法和 getLooper() 方法里是同一个锁。

IntentService 存在的意义

Service 一般用于处理后台耗时任务。

应用需求:一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。

这个需求可能用多个线程来处理,一个线程处理完一个子任务 --> 下一个线程处理完一个子任务 --> 下一个线程继续 ....

IntentService 就可以帮我们完成这个工作。而且,能够很好的管理线程,保证一个时间段里只有一个子线程处理一个子任务,而且是一个一个的完成任务,有序进行。

IntentService 的onCreate() 函数里会创建一个 HandlerThread(异步线程),然后onHandleIntent() 函数处理任务(是一个抽象函数,继承),耗时任务就在里面处理。

Service 的生命周期是后台管理的,IntentService extends Servicej, 所以IntentService 的生命周期也是后台管理的。所以 IntentService 的 onCreate() 函数是由后台调度的,以及 onStart() 函数。

走 onStart() 方法在子线程里发送一个消息。 再由 ServiceHandler 来处理 IntentService 里子线程发送的消息。

ServiceHandler 处理接收的消息,在 handleMessage里调用 onHandleIntent(我们重写)来处理消息。 

所以综上,我们在使用 IntentService 时,就可以 new 一个 IntentServcie的子类,实现 onHandleIntent 来处理耗时的后台任务。

消息处理完后调用 stopSelf(),service 自动停止 --> 内存自动释放

 Glide 生命周期

glide.with(context).from(url).into(imageView)

这里的 context --> fragment/activity 。当我们的 fragment/activity 生命周期结束时,Glide 也结束。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别偷我的猪_09

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值