Android之Handler進階

來源:互聯網
上載者:User

現在我們首先就有一個問題,我們使用myThreadHandler.sendEmptyMessage(0);發送一個message對象,那麼Handler是如何接收該message對象並處理的呢?我先畫一個資料結構圖:

 

 

從這個圖中我們很清楚可以看到調用sendEmptyMessage後,會把Message對象放入一個MessageQueue隊列,該隊列屬於某個Looper對象,每個Looper對象通過ThreadLocal.set(new Looper())跟一個Thread綁定了,Looper對象所屬的線程在Looper.Loop方法中迴圈執行從MessageQueue隊列讀取Message對象,並把Message對象交由Handler處理,調用Handler的dispatchMessage方法。

     現在我們再來看一下使用Handler的基本實現代碼:

               // 主線程中建立一個handler
                normalHandler = new Handler() {
                        public void handleMessage(android.os.Message msg) {
                                btnSendMsg2NormalHandler.setText("normalHandler");
                                Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--normalHandler handleMessage run...", Thread.currentThread()
                                                .getName()));
                        }
                };

...
//發送訊息到hanlder
myThreadHandler.sendEmptyMessage(0);

你現在已經很清楚了sendEmptyMessage到handleMessage的過程,途中經過Looper.MessageQueue隊列,轉由Looper所在的線程去處理了,這是一個非同步過程,當然Looper所在的線程也可以是sendEmptyMessage所在的線程。

     看了上面你也許還是迷惑不解,那麼什麼要Looper了,跟我們要用的Handler又有啥鳥關係呢?

     我在前面一直強調在主線程中使用handler,為什麼要這麼說呢,因為你在自己new一個新線程中去像我前面那樣簡單建立一個Handler,程式執行是會報錯的:

    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
     at android.os.Handler.<init>(Handler.java:121)
     at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)
     at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86)

    為什麼在主線程中不會報錯,而在自己新見的線程中就會報這個錯誤呢?很簡單,因為主線程它已經建立了Looper,你可以開啟ActivityThread的源碼看一下:

    public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }

    在main函數中它已經做了這個事情了,為什麼要調用 Looper.prepareMainLooper(); Looper.loop();我們可以進去看一下,在prepareMainLooper方法中建立了一個looper對象,並與當前進程進行了綁定,而在Looper.loop方法中,線程建立訊息迴圈機制,迴圈從MessageQueue擷取Message對象,調用  msg.target.dispatchMessage(msg);進行處理msg.target在myThreadHandler.sendEmptyMessage(0)設定進去的,因為一個Thead中可以建立多個Hander,通過msg.target保證MessageQueue中的每個msg交由發送message的handler進行處理,那麼Handler又是怎樣與Looper建立聯絡的呢,在Handler建構函式中有這樣一段代碼:

       mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;

在建立Handler時需要設定mLooper成員,Looper.myLooper是從當前線程中擷取綁定的Looper對象:

public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }

    若Looper對象沒有建立,就會拋異常"Can't create handler inside thread that has not called Looper.prepare()"
這跟我前面講的是一致的。所以我們在一個新線程中要建立一個Handler就需要這樣寫:

        class MyThread extends Thread {

                public void run() {               
                        Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread
                                        .currentThread().getName()));
                        // 其它線程中建立一個handler
                        Looper.prepare();// 建立該線程的Looper對象,用於接收訊息,在非主線程中是沒有looper的所以在建立handler前一定要使用prepare()建立一個Looper
                        myThreadHandler = new Handler() {
                                public void handleMessage(android.os.Message msg) {
                                        Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...", Thread
                                                        .currentThread().getName()));
                                }
                        };
                        Looper.myLooper().loop();//建立一個訊息迴圈,該線程不會退出
                }
        }

   現在,你應該對Handler的機制有所瞭解了吧,若有什麼疑問,歡迎在評論中提出

  在其它線程中Handler使用主線程的Looper

   前面我說了在新線程中要建立一個Handler需要調用Looper.prepare();也有另一種方法就是使用主線程中的Looper,那就不必建立Looper對象了:

                        threadMainLoopHandler =new Handler(Looper.getMainLooper()){
                                public void handleMessage(android.os.Message msg) {
                                        Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--threadMainLoopHandler handleMessage run...", Thread
                                                        .currentThread().getName()));                                       
                                }
                                //該handleMessage方法將在mainthread中執行
                        };

  這時候注意不要在handleMessage做太多的操作,因為它在主線程中執行,會影響主線程執行ui更新操作。

    使用Message.callback回調

   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
從dispatchMessage定義可以看出,如果Message對象內建callback對象,handler不會執行handleMessage方法而是執行message.callback中定義的run方法,當然callback還是在handler關聯的looper所綁定的線程中執行的。實際上Handler.post(Runnable r)方法就是把r添加到一個msg.callback的,也就是說,下面兩種寫法,沒有什麼區別:

1.使用Message.callback


                Message msg = Message.obtain(myThreadHandler,new Runnable() {
                   
                    @Override
                    public void run() {
                        Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",
                                Thread.currentThread().getName()));   
                    }
                });
                myThreadHandler.sendMessage(msg);
2.使用Handler.post


myThreadHandler.post(new Runnable() {
                   
                    @Override
                    public void run() {
                        Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",
                                Thread.currentThread().getName()));   
                    }
                });
註:對於Handler機制相關測試,我寫了一個測試類別

3.Handler對Activity finish影響。

在開發的過程中碰到一個棘手的問題,調用Activity.finish函數Acitivity沒有執行生命週期的ondestory函數,後面尋找半天是因為有一個handler成員,因為它有一個delay訊息沒有處理,調用Activity.finish,Activity不會馬上destory,所以記得在Ativity finish前清理一下handle中的未處理的訊息,這樣Activity才會順利的destory

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.