Android進階——深刻淺出Handler(一)

Android進階——深刻淺出Handler(一)

在學習Handler以前,首先要學習一些基本概念,這將對以後的學習有所幫助。html

  • 主線程:Main Thread,又叫UI線程(UI Thread)。Android應用執行的線程,因此叫主線程。負責分發事件到合適的UI窗口,也是應用和Android UI套件交互的線程。因此叫UI線程。
  • ANR:應用無響應(application not responding)。若是UI線程阻塞超過幾秒(如今通常是5秒),用戶就會看到應用無響應的Dialog。

下面用代碼來解釋什麼是主線程和ANR:java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
 //查看當前線程,
 Log.d("TAG", "current Thread's name:" +  Thread.currentThread().getName());
 
  //休眠UI線程5秒,製造ANR
        Thread thread = Thread.currentThread();
        try {
            thread.sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
}

ANR

參考:android

1.Processes and Threadsgit

2.What is the Android UiThread (UI thread)github

初識Handler

首先咱們經過一個Demo學習Handler的兩種基本用途:網絡

  • 1.延遲執行messages或runnables
  • 2.將A線程的操做入隊到B線程中

第一種用途比較好理解,好比延時finish掉啓動Activity。第二種是咱們在常常須要子線程中執行耗時操做,多是讀取文件或訪問網絡,可是咱們只能在主線程中更新UI。因此使用Handler來將更新UI的操做從子線程切換到主線程中執行。數據結構

1.繼承Handler類,實現handleMessage方法。通常不推薦使用非靜態內部類的方式實現,會致使內存泄露。具體緣由可參看我上一篇文章,Android開發——避免內存泄露。推薦使用靜態內部類和弱引用結合的方式來實現。以下所示。app

private static class MyHandler extends Handler{

        //對Activity的弱引用
        private final WeakReference<HandlerActivity> mActivity;

        public MyHandler(HandlerActivity activity){
            mActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = mActivity.get();
            if(activity==null){
                super.handleMessage(msg);
                return;
            }
            switch (msg.what) {
                case DOWNLOAD_FAILED:
                    Toast.makeText(activity, "下載失敗", Toast.LENGTH_SHORT).show();
                    break;
                case DOWNLOAD_SUCCESS:
                    Toast.makeText(activity, "下載成功", Toast.LENGTH_SHORT).show();
                    Bitmap bitmap = (Bitmap) msg.obj;
                    activity.imageView.setVisibility(View.VISIBLE);
                    activity.imageView.setImageBitmap(bitmap);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

    private final MyHandler mHandler = new MyHandler(this);

2.使用post方法發送Runnable對象,sendMessage方法來發送Message。less

下面的代碼經過postDelayed執行1秒後將Button顯示或隱藏的操做。在下載完圖片後使用sendMessage通知UI線程更新ImageView。async

//使用Handler進行延時操做
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setVisibility(imageView.getVisibility()==View.VISIBLE?View.GONE:View.VISIBLE);
                    }
                },1000);
                
    //使用子線程下載圖片
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = downloadImage();

                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        msg.what = bitmap==null?DOWNLOAD_FAILED:DOWNLOAD_SUCCESS;

                        handler.sendMessage(msg);
                    }
                }).start();

運行效果:

進一步瞭解Handler

提及Handler,就不得不提Message、MessageQueue以及Looper。搞清楚這四者之間的關係,Android開發中的Handler消息處理機制也掌握了個大概了。

Message

Message:包含描述和任意數據對象的消息,用於發送給Handler。

//獲取Message實例的方式
Message msg1 = Message.obtain();
//或
Message msg2 = Handler.obtainMessage();

學到這裏,可能有人會有疑問。不是可經過Handler發送Runnable對象的嗎?爲何這裏只提Message了呢?

好問題,讓咱們來查看post類方法的源碼。

public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
 
   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在postDelayed()方法內部,將Runnable對象經過getPostMessage()方法包裝成了一個Message對象。這樣就回答了咱們以前的疑問。對於Handler,無論是使用的是post類方法仍是send類方法,發送出去的都是Message對象。

public final class Message implements Parcelable {
    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;
    ...
    }

Message類中有這幾個成員變量描述消息,其中what是咱們定義的消息碼,爲了讓接收者能知道消息是關於什麼的。arg1和arg2用於發送一些integer類型的值。obj用於傳輸任意類型的值。

MessageQueue

MessageQueue:顧名思義,消息隊列。內部存儲着一組消息。對外提供了插入和刪除的操做。MessageQueue內部是以單鏈表的數據結構來存儲消息列表的。

獲取MessageQueue實例使用Looper.myQueue()方法。

查看源碼中的enqueueMessage()方法能看出MessageQueue是用單鏈表的數據結構來存儲消息的。

//插入Message操做
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 (;;) {
                    //從這裏可看出MessageQueue內部是以單鏈表的數據結構來存儲消息的
                    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;
    }

Looper

Looper:主要用於給一個線程輪詢消息的。線程默認沒有Looper,在建立Handler對象前,咱們須要爲線程建立Looper。

使用Looper.prepare()方法建立Looper,使用Looper.loop()方法運行消息隊列。

class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
  
            Looper.loop();
        }

這裏就有疑問了,以前咱們在主線程中,建立Handler以前並無建立Looper。這是爲何呢?查看Main Thread源碼(也就是ActivityThread)。

public static void main(String[] args) {
        ...
        //初始化Looper
        Looper.prepareMainLooper();

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

在ActivityThread的main方法中,初始化了Looper。這就是爲何咱們在主線程中不須要建立Looper的緣由。

好,今天的學習就到這。下次將繼續學習Handler、Message和Message之間的關係。

若是本文對你的開發有所幫助,而且你手頭剛好有零錢。

不如打賞我一杯咖啡,鼓勵我繼續分享優秀的文章。

相關文章
相關標籤/搜索