【每日一文】探索 Android 大殺器—— Handler

<b> 原文中文版鏈接: </b> https://github.com/xitu/gold-miner/blob/master/TODO/android-handler-internals.md
** 原文英文版鏈接: ** https://medium.com/@jagsaund/android-handler-internals-b5d49eba6977#.jqtvslkh9

如果你想要讓一個 Android 應(yīng)用程序反應(yīng)靈敏扣汪,那么你必須防止它的 UI 線程被阻塞歌懒。同樣地捶朵,將這些阻塞的或者計算密集型的任務(wù)轉(zhuǎn)到工作線程去執(zhí)行也會提高程序的響應(yīng)靈敏性桶蛔。然而色鸳,這些任務(wù)的執(zhí)行結(jié)果通常需要更新UI組件的顯示炒刁,但該操作只能在UI線程中去執(zhí)行。有一些方法解決了 UI 線程的阻塞問題眨补,例如阻塞隊列管削,共享內(nèi)存以及管道技術(shù)。Android 為解決這個問題撑螺,提供了一種自有的消息傳遞機制——Handler含思。Handler 是 Android Framework 架構(gòu)中的一個基礎(chǔ)組件,它實現(xiàn)了一種非阻塞的消息傳遞機制甘晤,在消息轉(zhuǎn)換的過程中含潘,消息的生產(chǎn)者和消費者都不會阻塞。
雖然 Handler 被使用的頻率非常高线婚,它的工作原理卻很容易被忽視遏弱。本篇文章深入地剖析 Handler 眾多內(nèi)部組件的實現(xiàn),它將會向您揭示 Handler 的強大之處酌伊,而不僅僅作為一個工作線程和 UI 線程通信的工具腾窝。

<b>圖片瀏覽示例</b>
讓我們從一個例子開始了解如何在應(yīng)用中使用 Handler。設(shè)想一個 Activity 需要從網(wǎng)絡(luò)上獲取圖片并顯示居砖。有幾種方式來做這件事虹脯,在下面的例子中,我們創(chuàng)建了一個新的工作線程去執(zhí)行網(wǎng)絡(luò)請求以獲取圖片奏候。

public class ImageFetcherActivity extends AppCompactActivity {
    class WorkerThread extends Thread {
        void fetchImage(String url) {
            // network logic to create and execute request
            handler.post(new Runnable() {
                @Override
                public void run() {
                    imageView.setImageBitmap(image);
                }
            });
        }
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // prepare the view, maybe setContentView, etc
        new WorkerThread().fetchImage(imageUrl);
    }
}

另一種方法則是使用 Handler Messages 來代替 Runnable 類循集。

public class ImageFetcherAltActivity extends AppCompactActivity {
    class WorkerThread extends Thread {
        void fetchImage(String url) {
            handler.sendEmptyMessage(MSG_SHOW_LOADER);
            // network call to load image
            handler.obtainMessage(MSG_SHOW_IMAGE, imageBitmap).sendToTarget();
        }
    }

    class UIHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SHOW_LOADER: {
                    progressIndicator.setVisibility(View.VISIBLE);
                    break;
                }
                case MSG_HIDE_LOADER: {
                    progressIndicator.setVisibility(View.GONE);
                    break;
                }
                case MSG_SHOW_IMAGE: {
                    progressIndicator.setVisibility(View.GONE);
                    imageView.setImageBitmap((Bitmap) msg.obj);
                    break;
                }
            }
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // prepare the view, maybe setContentView, etc
        new WorkerThread().fetchImage(imageUrl);
    }
}

在第二個例子中,工作線程從網(wǎng)絡(luò)獲取到一張圖片蔗草,一旦下載完成咒彤,我們需用使用下載好的 bitmap 去更新 ImageView 顯示內(nèi)容。我們知道不能在非 UI 線程中更新 UI 組件咒精,因此我們使用 Handler镶柱。Handler 扮演了工作線程和 UI 線程的中間人的角色。消息在工作線程中被 Handler 加入隊列模叙,隨后在 UI 線程中被 Handler 處理歇拆。

<b>深入了解 Handler</b>
Handler 由以下部分組成:
<li>Handler</li>
<li>Message</li>
<li>Message Queue</li>
<li>Looper</li>
<p></p>
我們接下來將學(xué)習各個組件以及他們之間的交互。
<b>Handler</b>

<b>[Handler[2]]</b> (https://developer.android.com/reference/android/os/Handler.html)是線程間傳遞消息的即時接口,生產(chǎn)線程和消費線程調(diào)用以下操作來使用 Handler:
<li>在消息隊列中創(chuàng)建故觅、插入或移除消息</li>
<li>在消費線程中處理消息</li>


android.os.Handler 組件

每個 Handler 都有一個與之關(guān)聯(lián)的 Looper 和消息隊列厂庇。有兩種創(chuàng)建 Handler 的方式:
<li>通過默認的構(gòu)造方法,使用當前線程中關(guān)聯(lián)的 Looper</li>
<li>顯式地指定使用的 Looper</li>
沒有指定 Looper 的 Handler 是無法工作的输吏,因為它無法將消息放到消息隊列中权旷。同樣地,它無法獲取要處理的消息贯溅。

public Handler(Callback callback, boolean async) {
    // code removed for simplicity
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException( “Can’t create handler inside thread that has not called Looper.prepare()”);
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

上面的代碼段展示了創(chuàng)建一個新的 Handler 的邏輯拄氯。Handler 在創(chuàng)建時檢查了當前的線程有沒有可用的 Looper 對象,如果沒有盗迟,它會拋出一個運行時的異常坤邪。如果正常的話熙含,Handler 則會持有 Looper 中消息隊列對象的引用罚缕。
注意:同一線程中的多個 Handler 分享一個同樣的消息隊列,因為他們分享的是同一個 Looper 對象怎静。
Callback 參數(shù)是一個可選參數(shù)邮弹,如果提供的話,它將會處理由 Looper 分發(fā)過來的消息蚓聘。

Message
Message[3]是容納任意數(shù)據(jù)的容器腌乡。生產(chǎn)線程發(fā)送消息給 Handler,Handler 將消息加入到消息隊列中夜牡。消息提供了三種額外的信息与纽,以供 Handler 和消息隊列處理時使用:
<li>what——一種標識符,Handler 能使用它來區(qū)分不同消息塘装,從而采取不同的處理方法</li>
<li>time——告知消息隊列何時處理消息</li>
<li>target——表示哪一個 Handler 應(yīng)當處理消息</li>


android.os.Message 組件
消息一般是通過 Handler 中以下方法來創(chuàng)建的:

public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

消息從消息池中獲取得到急迂,方法中提供的參數(shù)會放到消息體的對應(yīng)字段中。Handler 同樣可以設(shè)置消息的目標為其自身蹦肴,這允許我們進行鏈式調(diào)用僚碎,比如:

mHandler.obtainMessage(MSG_SHOW_IMAGE, mBitmap).sendToTarget();

消息池是一個消息體對象的 LinkedList 集合,它的最大長度是 50阴幌。在 Handler 處理完這條消息之后勺阐,消息隊列把這個對象返回到消息池中,并且重置其所有字段矛双。
當使用 Handler 調(diào)用 post 方法來執(zhí)行一個 Runnable 時渊抽,Handler 隱式地創(chuàng)建了一個新的消息,并且設(shè)置 callback 參數(shù)來存儲這個 Runnable议忽。

Message m = Message.obtain();
m.callback = r;

生產(chǎn)線程發(fā)送消息給 Handler 的交互

在上圖中懒闷,我們能看到生產(chǎn)線程和 Handler 的交互。生產(chǎn)者創(chuàng)建了一個消息,并且發(fā)送給了 Handler毛雇,隨后 Handler 將這個消息加入消息隊列中嫉称,在未來的某個時間,Handler 會在消費線程中處理這個消息灵疮。

Message Queue
Message Queue[4]是一個消息體對象的無界的 LinkedList 集合织阅。它按時序?qū)⑾⒉迦腙犃校钚〉臅r間戳將會被首先處理震捣。


android.os.MessageQueue 組件
消息隊列也通過 SystemClock.uptimeMillis 獲取當前時間荔棉,維護著一個阻塞閾值(dispatch barrier)。當一個消息體的時間戳低于這個值的時候蒿赢,消息就會被分發(fā)給 Handler 進行處理润樱。

Handler 提供了三種方式來發(fā)送消息:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

以延遲的方式發(fā)送消息,是設(shè)置了消息體的time字段為SystemClock.uptimeMillis()+delayMillis羡棵。
延遲發(fā)送的消息設(shè)置了其時間字段為 SystemClock.uptimeMillis() + delayMillis壹若。然而,通過 sendMessageAtFrontOfQueue() 方法把消息插入到隊首皂冰,會將其時間字段設(shè)置為 0店展,消息會在下一次輪詢時被處理。需要謹慎使用這個方法秃流,因為它可能會影響消息隊列赂蕴,造成順序問題,或是其它不可預(yù)料的副作用舶胀。
Handler 常與一些 UI 組件相關(guān)聯(lián)概说,而這些 UI 組件通常持有對 Activity 的引用。Handler 持有的對這些組件的引用可能會導(dǎo)致潛在的 Activity 泄露嚣伐√桥猓考慮如下場景:

public class MainActivity extends AppCompatActivity {
   private static final String IMAGE_URL = "https://www.android.com/static/img/android.png";

   private static final int MSG_SHOW_PROGRESS = 1;
   private static final int MSG_SHOW_IMAGE = 2;

   private ProgressBar progressIndicator;
   private ImageView imageView;
   private Handler handler;

   class ImageFetcher implements Runnable {
       final String imageUrl;

       ImageFetcher(String imageUrl) {
           this.imageUrl = imageUrl;
       }

       @Override
       public void run() {
           handler.obtainMessage(MSG_SHOW_PROGRESS).sendToTarget();
           InputStream is = null;
           try {
               // Download image over the network
               URL url = new URL(imageUrl);
               HttpURLConnection conn = (HttpURLConnection) url.openConnection();

               conn.setRequestMethod("GET");
               conn.setDoInput(true);
               conn.connect();
               is = conn.getInputStream();

               // Decode the byte payload into a bitmap
               final Bitmap bitmap = BitmapFactory.decodeStream(is);
               handler.obtainMessage(MSG_SHOW_IMAGE, bitmap).sendToTarget();
           } catch (IOException ignore) {
           } finally {
               if (is != null) {
                   try {
                       is.close();
                   } catch (IOException ignore) {
                   }
               }
           }
       }
   }

   class UIHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           switch (msg.what) {
               case MSG_SHOW_PROGRESS: {
                   imageView.setVisibility(View.GONE);
                   progressIndicator.setVisibility(View.VISIBLE);
                   break;
               }
               case MSG_SHOW_IMAGE: {
                   progressIndicator.setVisibility(View.GONE);
                   imageView.setVisibility(View.VISIBLE);
                   imageView.setImageBitmap((Bitmap) msg.obj);
                   break;
               }
           }
       }
   }

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       progressIndicator = (ProgressBar) findViewById(R.id.progress);
       imageView = (ImageView) findViewById(R.id.image);

       handler = new UIHandler();

       final Thread workerThread = new Thread(new ImageFetcher(IMAGE_URL));
       workerThread.start();
   }
}

在這個例子中,Activity 開啟了一個新的工作線程去下載并且在 ImageView 中展示圖片纤控。工作線程通過 UIHandler 去通知 UI 更新挂捻,這樣就會持有了對 View 的引用,以便更新這些 View 的狀態(tài)(切換可見性船万、設(shè)置圖片等)刻撒。

讓我們假設(shè)工作線程由于網(wǎng)絡(luò)差,需要很長的時間去下載圖片耿导。在工作線程下載完成之前銷毀這個 Activity 會導(dǎo)致 Activity 泄露声怔。在本例中,有兩個強引用關(guān)系舱呻,一個在工作線程和 UIHandler 之間醋火,另一個在 UIHandler 和 View 之間悠汽。這就阻止了垃圾回收機制回收 Activity 的引用。

現(xiàn)在芥驳,讓我們來看看另一個例子:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Ping";

    private Handler handler;

    class PingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "Ping message received");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new PingHandler();

        final Message msg = handler.obtainMessage();
        handler.sendEmptyMessageDelayed(0, TimeUnit.MINUTES.toMillis(1));
    }
}

在這個例子中柿冲,將按順序發(fā)生如下事件:

<li>PingHandler 被創(chuàng)建</li>
<li>Activity 發(fā)送了一個帶延遲的消息給 Handler,隨后消息加入到消息隊列中</li>
<li>Activity 在消息到達之前被銷毀</li>
<li>消息被分發(fā)兆旬,并被 UIHandler 處理假抄,輸出一條日志</li>
雖然起初看起來不那么明顯,但本例中的 Activity 也存在著泄露丽猬。

在銷毀 Activity 之后宿饱,Handler 應(yīng)當可以被垃圾回收,然而當創(chuàng)建了一個消息對象之后脚祟,它也會持有對 Handler 的引用:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

上面的 Android 代碼段表明谬以,所有被發(fā)送到 Handler 的消息最終都會觸發(fā) enqueueMessage 方法。注意到 Handler 的引用被顯式地賦給了 msg.target由桌,以此來告訴 Looper 對象當消息從消息隊列出隊時为黎,選擇哪一個 Handler 來對其進行處理。

消息加入消息隊列后沥寥,消息隊列就獲得了對消息的引用碍舍。它同樣有一個與之關(guān)聯(lián)的 Looper。一個自定義的 Looper 對象的生命周期一直持續(xù)到它被結(jié)束邑雅,然而主線程中的 Looper 對象在程序的生命周期內(nèi)一直存在。因此妈经,消息中持有的對 Handler 的引用會一直維持到該消息被消息隊列回收之前淮野,一旦消息被回收,它內(nèi)部的各字段事扭,包括目標 target 的引用都會被清空陆盘。

雖然 Handler 能存活很長時間跃巡,但是當 Activity 發(fā)生泄露時,Handler 不會被清空洞难。為了檢查是否發(fā)生泄露,我們必須檢查 Handler 是否在本類范圍內(nèi)持有 Activity 的引用揭朝。在本例中队贱,它確實持有:非靜態(tài)內(nèi)部類持有一個對其外部類的隱式引用。明確一點來說潭袱,PingHandler 沒有定義成一個靜態(tài)類柱嫌,所以它持有一個隱式的 Activity 引用。

通過結(jié)合使用弱引用和靜態(tài)類修飾符可以阻止 Handler 導(dǎo)致的 Activity 泄露屯换。當 Activity 被銷毀時编丘,弱引用允許垃圾回收器去回收你想要留存的對象(通常來說是 Activity)。在 Handler 內(nèi)部類前加入靜態(tài)修飾符可以阻止對外部類持有隱式引用。

讓我們來修改上例中的 UIHandler 來解決這個煩惱:

static class UIHandler extends Handler {
   private final WeakReference<ImageFetcherActivity> mActivityRef;

   UIHandler(ImageFetcherActivity activity) {
       mActivityRef = new WeakReference(activity);
   }

   @Override
   public void handleMessage(Message msg) {
       final ImageFetcherActivity activity = mActivityRef.get();
       if (activity == null) {
           return
       }

       switch (msg.what) {
           case MSG_SHOW_LOADER: {
               activity.progressIndicator.setVisibility(View.VISIBLE);
               break;
           }
           case MSG_HIDE_LOADER: {
               activity.progressIndicator.setVisibility(View.GONE);
               break;
           }
           case MSG_SHOW_IMAGE: {
               activity.progressIndicator.setVisibility(View.GONE);
               activity.imageView.setImageBitmap((Bitmap) msg.obj);
               break;
           }
       }
   }
}

現(xiàn)在嘉抓,UIHandler 的構(gòu)造方法中需要傳入 Activity索守,而這個引用會被弱引用包裝。這樣就允許垃圾回收器在 Activity 銷毀時回收這個引用抑片。當與 Activity 中的 UI 組件交互時蕾盯,我們需要從 mActivityRef 中獲得一個 Activity 的強引用。由于我們正在使用一個弱引用蓝丙,我們必須小心翼翼地去訪問 Activity级遭。如果僅僅能通過弱引用的方式去訪問 Activity,垃圾回收器也許已經(jīng)將其回收了渺尘,因此我們需要檢查回收是否發(fā)生挫鸽。如果確實被回收,Handler 實際上已經(jīng)與 Activity 無關(guān)了鸥跟,那么這條消息就應(yīng)該被丟棄丢郊。

雖然這個邏輯解決了內(nèi)存泄露問題,但仍舊存在一個問題医咨。Activity 已經(jīng)被銷毀枫匾,但垃圾回收器還沒來得及回收引用,依賴于操作系統(tǒng)運行時的狀況拟淮,這可能會使你的程序?qū)е聺撛诘谋罎⒏绍浴榻鉀Q這個問題,我們需要獲取 Activity 當前的狀態(tài)很泊。

讓我們更新 UIHandler 的邏輯來解決如上場景的問題:

static class UIHandler extends Handler {
    private final WeakReference<ImageFetcherActivity> mActivityRef;

    UIHandler(ImageFetcherActivity activity) {
        mActivityRef = new WeakReference(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final ImageFetcherActivity activity = mActivityRef.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            removeCallbacksAndMessages(null);
            return
        }

        switch (msg.what) {
            case MSG_SHOW_LOADER: {
                activity.progressIndicator.setVisibility(View.VISIBLE);
                break;
            }
            case MSG_HIDE_LOADER: {
                activity.progressIndicator.setVisibility(View.GONE);
                break;
            }
            case MSG_SHOW_IMAGE: {
                activity.progressIndicator.setVisibility(View.GONE);
                activity.imageView.setImageBitmap((Bitmap) msg.obj);
                break;
            }
        }
    }
}

現(xiàn)在角虫,我們可以概括消息隊列、Handler委造、生產(chǎn)線程的交互:


消息隊列戳鹅、Handler、生產(chǎn)線程的交互

在上圖中昏兆,多個生產(chǎn)線程提交消息到不同的 Handler 中枫虏。然而,不同的 Handler 都與同一個 Looper 對象關(guān)聯(lián)爬虱,因此所有的消息都加入到同一個消息隊列中隶债。這一點非常重要,Android 中創(chuàng)建的許多不同 Handler 都關(guān)聯(lián)到主線程的 Looper:

<li>The Choreographer:處理垂直同步與幀更新</li>
<li>The ViewRoot:
處理輸入和窗口事件饮潦,配置修改等等</li>
<li>The InputMethodManager:*處理鍵盤觸摸事件及其它</li>

小貼士:確保生產(chǎn)線程不會大量生成消息燃异,因為這可能會抑制處理系統(tǒng)生成消息。


主線程 Looper 分發(fā)消息的小示例
調(diào)試幫助:你可以通過附加一個 LogPrinter 到 Looper 上來 debug/dump 被 Looper 分發(fā)的消息:

final Looper looper = getMainLooper();
looper.setMessageLogging(new LogPrinter(Log.DEBUG, "Looper"));

同樣地继蜡,你可以 debug/dump 所有在消息隊列中等待的消息回俐,通過在與消息隊列相關(guān)聯(lián)的 Handler 上附加一個 LogPrinter 來實現(xiàn):

handler.dump(new LogPrinter(Log.DEBUG, "Handler"), "");

** Looper **
Looper[5]從消息隊列中讀取消息逛腿,然后分發(fā)給對應(yīng)的 Handler 處理。一旦消息超過阻塞閾仅颇,那么 Looper 就會在下一輪讀取過程中讀取到它单默。Looper 在沒有消息分發(fā)的時候會變?yōu)樽枞麪顟B(tài),當有消息可用時會繼續(xù)輪詢忘瓦。
每個線程只能關(guān)聯(lián)一個 Looper搁廓,給線程附加另外的 Looper 會導(dǎo)致運行時的異常。通過使用 Looper 類中的 ThreadLocal 對象可以保證每個線程只關(guān)聯(lián)一個 Looper 對象耕皮。
調(diào)用 Looper.quit() 方法會立即終止 Looper境蜕,并且會丟棄消息隊列中已經(jīng)通過阻塞閾的所有消息。調(diào)用 Looper.quitSafely() 方法能夠保證所有待分發(fā)的消息在列隊中等待的消息被丟棄前得到處理凌停。


Handler 與消息隊列和 Looper 直接交互的整體流程
Looper 應(yīng)在線程的 run 方法中初始化粱年。調(diào)用靜態(tài)方法 Looper.prepare() 會檢查線程是否與一個已存在的 Looper 關(guān)聯(lián)。這個過程的實現(xiàn)是通過 Looper 類中的 ThreadLocal 對象來檢查 Looper 對象是否存在罚拟。如果 Looper 不存在台诗,將會創(chuàng)建一個新的 Looper 對象和一個新的消息隊列。Android 代碼中的如下片段展示了這個過程赐俗。
注意:公有的 prepare 方法會默認會調(diào)用 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));
}

Handler 現(xiàn)在能接收到消息并加入消息隊列中,執(zhí)行靜態(tài)方法 Looper.loop() 方法會開始將消息從隊列中出隊阻逮。每次輪詢迭代器指向下一條消息粱快,接著分發(fā)消息到對應(yīng)目標的 Handler,然后回收消息到消息池中夺鲜。Looper.loop() 方法會循環(huán)執(zhí)行這個過程皆尔,直到 Looper 終止。Android 代碼中的如下片段展示了這個過程:

public static void loop() {
    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;
        }
        msg.target.dispatchMessage(msg);
        msg.recycleUnchecked();
    }
}

并沒有必要自己去創(chuàng)建關(guān)聯(lián) Looper 的線程币励。Android 提供了一個簡便的類做這件事——HandlerThread。它繼承 Thread 類珊拼,并且提供對 Looper 創(chuàng)建的管理食呻。下面的代碼描述了它的一般使用過程:

private final Handler handler;
private final HandlerThread handlerThread;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate();
    handlerThread = new HandlerThread("HandlerDemo");
    handlerThread.start();
    handler = new CustomHandler(handlerThread.getLooper());
}

@Override
protected void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}

onCreate() 方法構(gòu)造了一個 HandlerThread,當 HandlerThread 啟動后澎现,它準備創(chuàng)建 Looper 與它的線程關(guān)聯(lián)仅胞,隨后 Looper 開始處理 HandlerThread 的消息隊列中的消息。
注意:當 Activity 被銷毀時剑辫,結(jié)束 HandlerThread 是很重要的干旧,這個動作也會終止關(guān)聯(lián)的 Looper。

** 總結(jié) **
Android 中的 Handler 在應(yīng)用的生命周期中扮演著不可缺少的角色妹蔽。它是構(gòu)成半同步/半異步模式架構(gòu)的基礎(chǔ)椎眯。許多內(nèi)部和外部的代碼都依賴 Handler 去異步地分發(fā)事件挠将,它能以最小的代價去維持線程安全。
更深入地理解組件的工作方式能夠幫助解決疑難雜癥编整。這也能讓我們以最佳的方法使用組件的 API舔稀。我們通常將 Handler 作為工作線程和UI線程間的通信機制,但 Handler 并不僅限于此掌测。它出現(xiàn)在IntentService[6], 和Camera2[7]和許多其它的 API 中内贮。在這些 API 調(diào)用中,Handler 更多情形下是被用作任意線程間的通信工具汞斧。
在深入理解了 Handler 的原理后夜郁,我們能運用其構(gòu)建更有效率、更簡潔粘勒、更健壯的應(yīng)用程序竞端。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仲义,隨后出現(xiàn)的幾起案子婶熬,更是在濱河造成了極大的恐慌,老刑警劉巖埃撵,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赵颅,死亡現(xiàn)場離奇詭異,居然都是意外死亡暂刘,警方通過查閱死者的電腦和手機饺谬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谣拣,“玉大人募寨,你說我怎么就攤上這事∩” “怎么了拔鹰?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贵涵。 經(jīng)常有香客問我列肢,道長,這世上最難降的妖魔是什么宾茂? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任瓷马,我火速辦了婚禮,結(jié)果婚禮上跨晴,老公的妹妹穿的比我還像新娘欧聘。我一直安慰自己,他們只是感情好端盆,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布怀骤。 她就那樣靜靜地躺著费封,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晒喷。 梳的紋絲不亂的頭發(fā)上孝偎,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音凉敲,去河邊找鬼衣盾。 笑死,一個胖子當著我的面吹牛爷抓,可吹牛的內(nèi)容都是我干的势决。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼蓝撇,長吁一口氣:“原來是場噩夢啊……” “哼果复!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起渤昌,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤虽抄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后独柑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迈窟,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年忌栅,在試婚紗的時候發(fā)現(xiàn)自己被綠了车酣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡索绪,死狀恐怖湖员,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瑞驱,我是刑警寧澤娘摔,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站唤反,受9級特大地震影響晰筛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拴袭,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望曙博。 院中可真熱鬧拥刻,春花似錦、人聲如沸父泳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒸眠,卻和暖如春漾橙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背楞卡。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工霜运, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒋腮。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓淘捡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親池摧。 傳聞我的和親對象是個殘疾皇子焦除,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容