扒一扒面试必问的Handler

0.前言

Handler作为Android代码编写以及面试时经常遇到的内容,有必要花个时间整理一下,毕竟写过的东西印象会更加深刻。

1.什么是Handler?

1.1 定义

源码里面捞出来的内容,英文不难看懂。主要就是说每个Handler会和每个线程以及线程对应的消息队列相绑定。之后消息就可通过Handler在线程之间传递。

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}.  Each Handler
 instance is associated with a single thread and that thread's message queue.  When you create a new Handler, it is bound to the thread ,message queue of the thread that is creating it -- from that point on,
 it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
  

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

1.2 Handler的作用

Handler 的作用主要为两个:

  1. 让程序在未来的某个时间点去执行一些事情
  2. 事件跨线程传递执行(最主要的使用就是子线程让主线程去做一些刷新的事情)

1.3 Handler的基本用法

1.在未来某个时刻去执行runnable中的事件

handler.post(runnable,delayMillis);

2.不同消息间传递事件

android.os.Handler handler = new Handler(){
  @Override
  public void handleMessage(final Message msg) {
    //这里接受并处理消息
  }
};
//发送消息
handler.sendMessage(message);

2.Handler基本原理

2.1 Handler原理相关的类

  • Handler
  • Looper
  • MessageQueue
  • Messagre

2.2 Handler原理概述

首先放一张Handler消息处理的原理图

根据图一步步梳理Handler实现机制

2.3 Handler工作流程

2.3.1 创建Handler

首先第一步是创建Handler,Handler的构造函数主要有如下几个

其中的参数如下

  • boolean async

    async 该参数确定用Handler发送的消息是否要设置成异步消息,如果为ture设置为异步消息,异步消息可和同步阻塞一起配合使用,典型的例子是界面的定时刷新。

  • Callback callback

    设置了该参数,那么Handler在dispatchMessage回调时会优先调用callback中的代码,若返回为true,则不再执行handleMessage的回调

/**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

  • Looper looper

    设置该Hanlder对应的Looper参数,对于每个Handler,都有一个对应的Looper,每个Looper有其对应的MessageQueue。如果没有显式的设置Handler的Looper,那么Handler默认会取该线程对应的Looper赋给该Handler。

2.3.2 Handler发送消息

Handler发送消息主要调用的是sendMessage方法。

sendMessage(@NonNull Message msg)

发送消息的方法有许多不同的名称和参数。但是,你从源码一步步看最后都会指向如下的这段代码

public boolean sendMessageAtTime(@NonNull 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);
    }

看这段代码我们会发现,其主要的操作就是调用压队列的方法。传入的参数是队列,消息以及对应的事件

2.3.3 MessageQueue压入消息

该方法首先是对Message 实例进行各种参数的设置,最主要的就是target参数,该参数用于确定消息从MessageQueue中取出后去调用哪个Handler的dispatchMessage,从而进行消息的处理。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

设置完参数,Handler的enqueueMessage就会去调用MessageQueue的enqueueMessage方法,代码如下

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

从代码中我们能发现,消息队列主要是通过链表的形式进行的存储。这段代码的所处理的事情就是根据Message中的when参数从列表中来找到需要插入的点,并把Message进行参入。

2.3.4 Looper取出消息

Looper取出消息主要是调用了Looper类的loop方法,该方法里面有一个for(;;)循环,不停的从MessageQueue中取出消息。

 for (;;) 
    {
        Message msg = queue.next();
        ……
    }

2.3.5 Handler处理消息

当消息取出以后,即会调用

 msg.target.dispatchMessage(msg);


来处理消息,这个target即时开始enqueMessage时塞入的Handler。

从Handler中我们可以看到,这dispatchMessage(Message msg)方法。

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

其中handleMessage(msg)就是我们自定义Handler时的回调,这样消息就走完了整个流程。

3.Handler延伸

3.1 Q Handler 为什么会导致内存泄漏,该如何处理这个内存泄漏问题?

A: Handler的普通用法如下:

  Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

        }
    };

由于Handler是通过匿名内部类的方式实现的,所以其会对外部类有引用(通常为Activity),这就会引起内存泄漏。

通常的解决如下

把Handler定义成静态内部类,如Handler中需要对外部类的参数有引用,那么可以使用弱引用,示例代码如下

 private static class MyHandler extends Handler{
        //持有弱引用HandlerActivity,GC回收时会被回收掉.
        private final WeakReference mActivty;
        public MyHandler(HandlerActivity activity){
            mActivty =new WeakReference(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity=mActivty.get();
            super.handleMessage(msg);
            if(activity!=null){
                //执行业务逻辑
            }
        }
    }

3.2 Q Handler类中有个Callback的接口,请问这个接口有什么用?

A: 这个的设计主要是为了解决Handler内存泄漏的问题。

可参考如下代码:

Handler handler = new Handler(new Handler.Callback(

@Override

public boolean handleMessage(Message msg) {

if (msg.what ==1){

textView.setText("Hello!");

return true;

    }

return false;

));


该接口的调用主要出现在dispatchMessage中,从此处的代码可知道,如果mCallback变量没有定义,变量为空,会走Handler系统的方法hadlerMessage(Message msg),而如果mCallback变量定义了,那么其会运行到mCallback

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

3.3 Q Handler中的普通消息,异步消息和消息屏障是什么关系?有什么作用?

MessageQueue中的Message一共有三种类型,即普通消息,异步消息和消息屏障。区别是消息屏障它的target为null。而普通消息和异步消息是通过setAsynchronous为true来进行区分的。

在设置消息屏障后,异步消息具有优先处理的权利。界面的定时刷新就是用的这个机制

3.4 Q Message的target是什么时候注入的?

捞取Handler的源码发现如下这段

 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }


即哪个Handler把Message存入的消息队列,最后回调的就是这个Handler对应的dispatchMessage 方法。

3.5 Q Looper会不停的从消息队列中取消息吗?

不会,若MessageQueue中消息已经取完或者消息要在之后的某个事件才会促发,则会调用native方法 nativePollOnce(long ptr, int timeoutMillis),使主线程处于等待状态。当调用了往消息队列塞入消息时,则会调用nativeWake(long ptr)方法唤醒主线程,所以尽管,loop方法中有个for死循环,但是这不会导致Looper不停从消息队列取数据。

3.6 Q Handler的post(Runnable r) 方法是如何实现Runnable中事件的执行的?

post方法从本质上来说也是用的Message消息传递的流程。

 public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

从如下两端代码可看出其是对runnable进行了封装,把runnable塞进了Message中。而当Handler获得消息后,其会取出msg.callback参数,然后运行里面的runnable方法。

3.7 Q 平时代码编写中如何获得Message对象比较好?

用Message.obtain()获取比较好,避免了gc导致的性能消耗。

3.8 Q IdleHandler是什么,有什么作用?

IdleHandler是在主线程消息队列空闲时,会被取出执行的对象。可在一些页面优化的情景下使用。

你可能感兴趣的:(扒一扒面试必问的Handler)