1. 程式人生 > >Android之非同步訊息處理機制詳解

Android之非同步訊息處理機制詳解

一、在子執行緒中更新UI概述

和許多其他的GUI 庫一樣,Android 的UI 也是執行緒不安全的。也就是說,如果想要更新應用程式裡的UI 元素,則必須在主執行緒中進行,否則就會出現異常。但是有些時候,我們必須在子執行緒裡去執行一些耗時任務,然後根據任務的執行結果來更新相應的UI 控制元件,這該如何是好呢?對於這種情況,Android 提供了一套非同步訊息處理機制,完美地解決了在子執行緒中進行UI 操作的問題。

二、Handler與Message的基本用法

Handler主要接收子執行緒傳送的資料, 並用此資料配合主執行緒更新UI,用來跟UI主執行緒互動用。比如可以用handler傳送一個message,然後在handler的執行緒中來接收、處理該訊息,以避免直接在UI主執行緒中處理事務導致影響UI主執行緒的其他處理工作,Android提供了Handler作為主執行緒和子執行緒的紐帶;也可以將handler物件傳給其他程序,以便在其他程序中通過handler給你傳送事件;還可以通過handler的延時傳送message,可以延時處理一些事務的處理。
我們對上一篇

Android之AsyncTask詳解中的案例稍作修改:

public class MainActivity extends AppCompatActivity {

    private ProgressDialog progressDialog;//下載進度條
    private Button button;
    private ImageView imageView;

    //圖片下載地址
    private final String IMAGE_URL = "http://img5q.duitang.com/uploads/item/201504/07/20150407H0747_rPsRA.thumb.700_0.jpeg"
; private final int IS_DOWNING = 0; private final int IS_FINISH = 1; private Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == IS_FINISH){ Bitmap bitmap = (Bitmap)msg.obj; imageView.setImageBitmap(bitmap); progressDialog.dismiss(); }else
if(msg.what == IS_DOWNING ){ int value = (int)msg.obj; progressDialog.setProgress(value); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new MyThread()).start(); progressDialog.show(); } }); imageView = (ImageView) findViewById(R.id.imageView); progressDialog = new ProgressDialog(this); progressDialog.setTitle("提示"); progressDialog.setMessage("正在下載,請稍後..."); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); } public class MyThread implements Runnable{ @Override public void run() { Bitmap bitmap = downBitmap(IMAGE_URL); Message message = Message.obtain(); message.obj = bitmap; message.what = IS_FINISH; myHandler.sendMessage(message); } } /** * 下載圖片 ,並保持為Bitmap * @param url * @return */ private Bitmap downBitmap(String url){ Bitmap bitmap = null; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); InputStream inputStream = null; try { /* *2016-11-01註釋:在Android 5.0之後,HttpClient被HttpURLConnecetion替代,後來在Android 6.0完全被捨棄, *如需要HttpURLConnecetion的實現方式,移步 *到http://blog.csdn.net/wei_zhi/article/details/52997246 */ HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = httpClient.execute(httpGet); if (httpResponse.getStatusLine().getStatusCode() == 200) { inputStream = httpResponse.getEntity().getContent(); long file_len = httpResponse.getEntity().getContentLength(); int len = 0; byte[] data = new byte[1024]; int total_len = 0; while ((len = inputStream.read(data)) != -1) { total_len += len; int value = (int) ((total_len / (float) file_len) * 100); //publishProgress(value); Message message = Message.obtain(); message.obj = value; message.what = IS_DOWNING; myHandler.sendMessage(message); outputStream.write(data, 0, len); } byte[] result = outputStream.toByteArray(); bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); } } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return bitmap; } }

執行結果:

1

2

3

三、解析非同步訊息處理機制

Android 中的非同步訊息處理主要由四個部分組成,Message、Handler、MessageQueue 和Looper。下面對這四個部分進行一下簡要的介紹:

(1)Message
Message 是線上程之間傳遞的訊息,它可以在內部攜帶少量的資訊,用於在不同執行緒之間交換資料。上一小節中我們使用到了Message 的what 欄位,除此之外還可以使用arg1 和arg2 欄位來攜帶一些整型資料,使用obj 欄位攜帶一個Object 物件。

(2)Handler
Handler 顧名思義也就是處理者的意思,它主要是用於傳送和處理訊息的。傳送訊息一般是使用Handler 的sendMessage()方法,而發出的訊息經過一系列地輾轉處理後,最終會傳遞到Handler 的handleMessage()方法中。

在使用Handler之前,我們都是初始化一個例項,比如用於更新UI執行緒,我們會在宣告的時候直接初始化,或者在onCreate中初始化Handler例項:

private Handler mHandler = new Handler()
    {
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
            case value:

                break;

            default:
                break;
            }
        };
    };

(3)MessageQueue
MessageQueue 是訊息佇列的意思,它主要用於存放所有通過Handler 傳送的訊息。這部分訊息會一直存在於訊息佇列中,等待被處理。每個執行緒中只會有一個MessageQueue物件。

(4)Looper
Looper 是每個執行緒中的MessageQueue 的管家,呼叫Looper 的loop()方法後,就會進入到一個無限迴圈當中,然後每當發現MessageQueue 中存在一條訊息,就會將它取出,並傳遞到Handler 的handleMessage()方法中。每個執行緒中也只會有一個Looper 物件。

對於Looper主要是prepare()和loop()兩個方法:

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
} 

sThreadLocal是一個ThreadLocal物件,可以在一個執行緒中儲存變數。Looper 就是儲存在sThreadLocal裡面。這個方法被呼叫後,首先會判斷當前執行緒裡面有沒有 Looper物件,如果沒有就會建立一個 Looper 物件,如果存在則會丟擲異常。可見,prepare()方法,不能被呼叫兩次。這就保證了一個執行緒只有一個Looper物件。

然後我們看看 loop() 方法:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

這個方法先呼叫 myLooper() 方法,得到 sThreadLocal 中儲存的 Looper 物件,並得到 looper 物件對應的 MessageQueue 物件,然後就進入無限迴圈。該迴圈主要包括:取出一條訊息,如果沒有訊息則阻塞; 呼叫 msg.target.dispatchMessage(msg);把訊息交給msg的target的dispatchMessage方法去處理。

Looper主要作用:
(1)與當前執行緒繫結,保證一個執行緒只會有一個Looper例項,同時一個Looper例項也只有一個MessageQueue。
(2)loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。

瞭解了Message、Handler、MessageQueue 以及Looper 的基本概念後,我們再來對非同步訊息處理的整個流程梳理一遍。首先需要在主執行緒當中建立一個Handler 物件,並重寫handleMessage()方法。然後當子執行緒中需要進行UI 操作時,就建立一個Message 物件,並通過Handler 將這條訊息傳送出去。之後這條訊息會被新增到MessageQueue 的佇列中等待被處理,而Looper 則會一直嘗試從MessageQueue 中取出待處理訊息,最後分發回Handler的handleMessage()方法中。由於Handler 是在主執行緒中建立的,所以此時handleMessage()方法中的程式碼也會在主執行緒中執行,於是我們在這裡就可以安心地進行UI 操作了。整個非同步訊息處理機制的流程示意圖如圖所示。

4

總結:

(1)首先Looper.prepare()在本執行緒中儲存一個Looper例項,然後該例項中儲存一個MessageQueue物件,因為Looper.prepare()在一個執行緒中只能呼叫一次,所以MessageQueue在一個執行緒中只會存在一個。

(2)Looper.loop()會讓當前執行緒進入一個無限迴圈,不斷從MessageQueue的例項中讀取訊息,然後回撥msg.target.dispatchMessage(msg)方法。

(3)Handler的構造方法,會首先得到當前執行緒中儲存的Looper例項,進而與Looper例項中的MessageQueue想關聯。

(4)Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。

(5)在構造Handler例項時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終呼叫的方法。

相關推薦

Android非同步訊息處理機制

一、在子執行緒中更新UI概述 和許多其他的GUI 庫一樣,Android 的UI 也是執行緒不安全的。也就是說,如果想要更新應用程式裡的UI 元素,則必須在主執行緒中進行,否則就會出現異常。但是有些時候,我們必須在子執行緒裡去執行一些耗時任務,然後根據任務的執

Android非同步訊息處理機制及原始碼分析

PS一句:最終還是選擇CSDN來整理髮表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown語法了,牛逼啊! 【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】 最近相對來說比較閒,加上養病,所

Android非同步訊息處理機制

關於Handler例項化的一些關鍵資訊,具體如下: 在主執行緒中可以直接建立Handler物件,而在子執行緒中需要先呼叫Looper.prepare()才能建立Handler物件,否則執行丟擲”

Android基礎非同步訊息處理機制

今天講述一下Android的非同步訊息處理機制,說到非同步,我們肯定會想到繼承Thread,實現Runnable來處理耗時操作,然後再發訊息去處理對應的業務邏輯。相信大家對下面的程式碼非常熟悉。 public class MainActivity exte

Android Handler 非同步訊息處理機制的妙用 建立強大的圖片載入類

                最近建立了一個群,方便大家交流,群號:55032675上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後,提出可以把非同步訊息處理

Android Handler 非同步訊息處理機制三:妙用手法 建立強大的圖片載入類

上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後,提出可以把非同步訊息處理機制不僅僅是在MainActivit

android非同步訊息處理機制及Handler

這時就需要Handler了,修改MainActivity如下:public class MainActivity extends AppCompatActivity { public static final int UPDATE_TEXT =1 ; @Override protec

Android Handler 非同步訊息處理機制的妙用 建立強大的圖片載入類【轉】

上一篇部落格介紹了Android非同步訊息處理機制,如果你還不瞭解,可以看:Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係 。那篇部落格的最後,提出可以把非同步訊息處理機制不僅僅是在MainActiv

Android面試-非同步訊息處理機制

【>>>Handler:{    什麼是handler、handler的使用方法、handler機制的原理、handler引起的記憶體洩漏以及解決辦法<非靜態內部類使用外部類的引用造成的。>    沒法在子執行緒建立Handler:需要一個訊息佇

Android——解析非同步訊息處理機制

Android中的非同步訊息處理主要由四部分組成,Message、Handler、MessageQueue、Looper. 1.Message Message是執行緒之間傳遞的訊息,它可以在內部攜帶少

android 非同步訊息處理機制 — AHandler

1. 引入 ALooper、AHandler、AMessage 在 android multimedia stagefright 的框架程式碼中,通篇都是這幾個類的身影,所以熟悉 android 多媒體框架的第一步必須理解這幾個類的含義。 這幾個類是為了實現非同步訊息機制而設計的

Android非同步訊息處理機制:Looper、Handler、Message

1 簡介 Handler,Looper,Message這三者都與Android非同步訊息處理執行緒相關, Looper:負責建立一個MessageQueue,然後進入一個無限迴圈體不斷從該MessageQueue中讀取訊息; Handler:訊息建立者,一個或者多個

Android中的非同步訊息處理機制

這也是Android中老生常談的一個話題了,它本身並不是很複雜,可是面試官比較喜歡問。本文就從原始碼再簡單的理一下這個機制。也可以說是理一下Handler、Looper、MessageQueue之間的關係。 單執行緒中的訊息處理機制的實現 首先我們以Looper.java原始碼中給出的一個例子來

Android面試系列非同步訊息處理相關

我們在平時的專案開發中,肯定會遇到處理非同步任務的場景。因為Android中的UI執行緒是不安全的,我們需要更新ui的話就必須在ui執行緒上進行操作。否則就會拋異常。 這個時候我們就需要用到非同步訊息處理了 比如,在子執行緒中請求資料,拿到資料後告訴ui執行

(轉載)Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係

很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什麼關係?本篇部落格目的首先為大家從原始碼角度介紹3者關係,然後給出一個容易記憶的結論。 1、 概述 Handler 、 Looper 、Message

深入理解Android非同步訊息處理機制

一。概述   Android 中的非同步訊息處理主要分為四個部分組成,Message、Hndler、MessageQueue 和 Looper。其關係如下圖所示:     1. Message 是執行緒之間傳遞的訊息,它可以在內部攜帶少量資訊,用於在不同執行緒之間交換資料。   2. Messag

android非同步訊息處理機制

 android非同步訊息處理主要由四部分組成:Handler,Looper,Message,MessageQueue​ Message:執行緒之間傳遞的訊息,可以在內部攜帶少量訊息 MessageQueue: Looper:每個執行緒有且最多隻能有一個Looper物件

Android非同步訊息處理機制的原始碼分析

1、背景 相信做過一段時間的Android開發都瞭解,當我們在子執行緒中更新UI時會丟擲異常,導致程式崩潰,Android4.0之後只允許在UI執行緒中更新介面,但是我們也不能再UI執行緒中處理耗時操作,那樣會導致應用程式無響應(即出現ANR)。 那如果想解

Android非同步訊息處理機制 handler

我們都知道,Android UI是執行緒不安全的,如果在子執行緒中嘗試進行UI操作,程式就有可能會崩潰。相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也是早已爛熟於心,即建立一個Message物件,然後藉助Handler傳送出去,之後在Handler的han

Android非同步訊息處理機制原始碼分析

宣告:本文是參考了以下幾位大神的文章,自己按照自己的思維習慣整理的筆記,並新增一些相關的內容。如有不正確的地方歡迎留言指出,謝謝! 郭霖部落格 鴻洋部落格 任玉剛《Android開發藝術探索》 一. Andoid訊息機制概述