面試 - 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章