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,可以延時處理一些事務的處理。
我們對上一篇
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;
}
}
執行結果:
三、解析非同步訊息處理機制
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 操作了。整個非同步訊息處理機制的流程示意圖如圖所示。
總結:
(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訊息機制概述