欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-14 17:57:37
...

Handler定义以及作用

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

Handler使用流程图

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

Handler原理图

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

几个关键的类:

  • 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()的方法
    面试 - handle使用及原理(1)看一下源码几种方法的区别:
 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()的方法
面试 - handle使用及原理(1)
看一下源码几种方法的区别:

    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

相关标签: Handler