面试 - handle使用及原理(1)

Handler定义以及作用

handlerandorid的一套消息传递机制,用于跨线程通信,主要用于工作线程与主线程间的交互。andoridUI操作需要在主线程上操作,一般耗时都放到其它的子线程下操作。耗时操作返回的结果在UI线程先的展示需要跨线程通信 这个时候就可以使用handler来通信了

Handler使用流程图

handler使用展示.png

Handler原理图

自己画的handler机制的调用流程.png

几个关键的类:

  • Message:数据单元,MessageQueue的一个个数据。
  • LooperMessageQueueHandler的通信中间人。两个作用:不断循环从MessageQueue中取出Message,将Message发送给对应的Handler
  • MessageQueue:数据结构(先进先出)存储Message
  • Handler:线程间通行的中间人,Message信息的逻辑处理者,将Message发送到MessageQueue,处理Looper发送过来的Message

基础使用

public class MainActivity extends AppCompatActivity {
    //关键代码1
    private static class MyHander extends Handler{
        private final WeakReference<MainActivity> mActivity  ;
        public MyHander(MainActivity activity){
            mActivity = new WeakReference<MainActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
           if (null != mActivity.get()) {
                switch (msg.what){
                    case 1:
                      //关键代码3
                        mActivity.get().toNotify();
                        break;
                        default:
                            break;
                }
           }
        }
    }
    MyHander myHander;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     //关键代码2
        myHander = new MyHander(this);
        Button button = findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
          //关键代码3
                 Message message = myHander.obtainMessage();
                message.what = 1;
                message.obj = getString(R.string.text);
                myHander.sendMessageDelayed(message,5000);
            }
        });
    }

    public void toNotify() {
      ....逻辑操作
    }

}

关键代码1: 先定义MyHanlder继承Handler
关键代码2:新建myHandler对象,将MainActivity传入MyHandler,MyHandler弱引用MainActivity,所以MyHandler # handleMessage()处理需要if (null != mActivity.get())做一下对象null判断
关键代码3:构建Message,可以从缓存池中获取,也可以直接自己new Message,并发送myHander.sendMessageDelayed(message,5000)

其实平时的写法有些同学是在MainActivity中直接定义匿名内部类MyHander那种做法会调来内存泄漏的风险。因为匿名内部类引用着外部类,导致外部类被挟持,这就有可能导致内存泄漏了。
上面这种用法可以避免handler挟持了外部类MainActivity的引用

handler发送数据的两种不同的形式

  1. sendxxx()的方法
    sendxxx()的方法.png
    看一下源码几种方法的区别:
 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
  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);
    }
 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
   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);
    }

从上面可以看到,方法的最后都是调用到了sendMessageAtTime(Message msg, long uptimeMillis),看了一下上面的方法大家都懂各个方法的区别了。
主要是要注意一下使用的时候sendMessageAtTime(Message msg, long uptimeMillis)sendMessageDelayed(Message msg, long delayMillis)的区别,一个是当前的时刻,一个 当前的时刻+delayMillis

2.postxxx()的方法

postxxx()的方法.png

看一下源码几种方法的区别:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }
   public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    public final boolean postDelayed(Runnable r, Object token, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    }

    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

对应使用了sendxxx的方法,主要看一下getPostMessage()这个方法

这里有个坑 要记得看一下:Handler.post(Runnable)其实就是生成一个what = 0Message
如果你先调用Handler.post(Runnable)再调用发送任何一条what = 0Message会导致原来的被remove掉,从而看到一脸懵逼 具体的等下代码解析再看一下https://www.cnblogs.com/coding-way/p/5110125.html

源码解析

从主线程开始入手吧~

ActivityThread

public final class ActivityThread extends ClientTransactionHandler {
  final H mH = new H();
  final Handler getHandler() {
        return mH;
    }
  class H extends Handler {
  ......
  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case BIND_APPLICATION:
                     ......
                    break;
                }

               case EXIT_APPLICATION:
                    ......
                    break;
          
         }
   }
   public static void main(String[] args) {
   
        Process.setArgV0("<pre-initialized>");
        ......
        //关键代码1 
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        //关键代码2
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        .......
        //关键代码3
        Looper.loop();
  }

关键代码1:先调用Looper.prepareMainLooper()
关键代码2:获取sMainThreadHandler,获取ActivityThreadmH对象,mH对象实现了handleMessage(Message msg)方法
关键代码3:Looper.loop()开启循环

我们先看一下关键代码1 看一下Looper这个类以及对象的调用方法()

Looper(部分源码)

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    public static void prepare() {
        prepare(true);
    }

    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));
    }
     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;
          ......
           for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
              ......
            try {
                msg.target.dispatchMessage(msg);
            }
            ......
        }
    }
}
  1. Looper.prepareMainLooper():这个方法的相关调用流prepare(false)->sThreadLocal.set(new Looper(quitAllowed));->myLooper()->sThreadLocal.get()
    这里产生了一个looper并设置到sThreadLocal中,获取的时候也是从sThreadLocal中去获取。而且在prepare(false)中做判断,如果已经存在就不能再调用该方法,也就是说 一个线程中只能存在一个Looper,且一个Looper只能拥有一个mQueue,则说明LooperMessageQueue是一对一关系
  2. Looper.loop():这个方法的相关调用流程me->me.mQueue->(for (;;)-> queue.next()->msg.target.dispatchMessage(msg))
    开启死循环不断的轮训MessageQueue,msg.target其实返回的是Handler,使用Handler发送数据
    (关于ThreadLocal相关的,后面再加以补充)

接下来我们看一下Handler发送数据dispatchMessage这个方法

Handler (部分源码)

public class Handler {
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    public void handleMessage(Message msg) {
    }
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

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

这里你可以看到dispatchMessage(Message msg)中有三种不同的调用

  1. handleCallback(msg):这个是处理Handler.postxxx(Runnable r,xx)发送的信息处理,最终回到用到rrun方法里面做逻辑处理
  2. handleMessage(msg)mCallback.handleMessage(msg):都是用于处理Handler.sendxxx()发送的信息,不同点在于,实例化Handler有没有传入mCallback对象,有的话则在mCallback.handleMessage进行逻辑处理,没有的话则在handleMessage进行逻辑处理

看一下Message这个类的一些属性

Message(部分源码)

public final class Message implements Parcelable {
  public int what;
  public int arg1;
  public Object obj;
  long when;
  Bundle data;
  Handler target;
  Runnable callback;
  Message next;
}

Message拥有Handler对象,所以来自不同的Handler对象发送的Message信息将自己也传入进来。

总结

Handler并将本身传入到Messag中并发送到MessageQueue中,Looper开启轮训不断轮训MessageQueue的消息,将Message取出并使用Handler对象回调其本身的方法去进行逻辑操作。
Looper: 在第一次实例化的时候存到ThreadLocal变量中,并且获取的时候有则返回无则创建,所以一个线程只存在一个Looper。
MessageQueue: 是在Looper的构造函数中创建的,并且作为其成员变量,一个线程有且只有一个MessageQueue
Handler: 可以创建多个,线程中并没有限制其创建的个数,并且在Message中挟持着对应的Handler,所以不同的Message会对应交由自己的挟持的Handaler进行逻辑的操作

Handler各种面试题一览

(大家可以参考一下网友的这篇文章)https://blog.csdn.net/feather_wch/article/details/81136078