Android UI是線程不安全的,如果在子線程中嘗試進行UI操作,程序就有可能會崩潰。Handler的使用過程很簡單,通過它可以輕松地將一個任務切換到Handler所在的線程中去執(zhí)行
Handler 基本使用
- Activity中定義一個Handler的實例,處理消息
public Handler mHandle = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Bitmap bitmap = (Bitmap) msg.obj;
image.setImageBitmap(bitmap);
break;
default:
break;
}
};
};
- 異步獲取網(wǎng)絡圖片
private void getNetWorkImage() {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL imageUrl = new URL("https://www.baidu.com/img/bdlogo.png");
HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
conn.setConnectTimeout(10000);
conn.setRequestMethod("GET");
if(conn.getResponseCode() == 200) {
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
Message msg = mHandle.obtainMessage();
msg.what = 1;
msg.obj = bitmap;
mHandle.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
- 在activity銷毀時取消所有的消息(內(nèi)存泄露)
@Override
protected void onDestroy() {
super.onDestroy();
mHandle.removeCallbacks(null);
}
- 內(nèi)存泄露
內(nèi)部類會持有外部類的引用,在Activity銷毀時會造成內(nèi)存泄露,所以在destroy時remove所有的消息
也可以使用靜態(tài)內(nèi)部類,(缺點比較明顯,不能方便的使用外部類變量)
static class TestHandler extends Handler {
WeakReference<Activity> mActivityReference;
TestHandler(Activity activity) {
mActivityReference = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
HandlerThread 基本使用
HandlerThread就是Thread、Looper和Handler的組合實現(xiàn)
public class TestHanderThread extends Activity {
private HandlerThread mHandlerThread = null;
private Handler mThreadHandler = null;
private Handler mUiHandler = null;
private static final String TAG = "TestHanderThread";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
}
private void initData() {
Log.i(null, "Main Thread id="+Thread.currentThread().getId());
mHandlerThread = new HandlerThread("HandlerWorkThread");
//必須在實例化mThreadHandler之前調(diào)運start方法,原因上面源碼已經(jīng)分析了
mHandlerThread.start();
//將當前mHandlerThread子線程的Looper傳入mThreadHandler,使得
//mThreadHandler的消息隊列依賴于子線程(在子線程中執(zhí)行)
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "在子線程中處理!id="+Thread.currentThread().getId());
//從子線程往主線程發(fā)送消息
mUiHandler.sendEmptyMessage(0);
}
};
mUiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "在UI主線程中處理!id="+Thread.currentThread().getId());
}
};
//從主線程往子線程發(fā)送消息
mThreadHandler.sendEmptyMessage(1);
}
}
log.png
Hander、MessageQueue、Looper
Looper負責的就是創(chuàng)建一個MessageQueue,然后進入一個無限循環(huán)體不斷從該MessageQueue中讀取消息,而消息的創(chuàng)建者就是一個或多個Handler
-
Message:消息體,用于裝載需要發(fā)送的對象。
Message msg = handler.obtainMessage();
獲取Message.png
消息會進行復用減少內(nèi)存開銷
- handler:它直接繼承自Object。作用是:在子線程中發(fā)送Message或者Runnable對象到MessageQueue中;在UI線程中接收、處理從MessageQueue分發(fā)出來的Message或者Runnable對象。發(fā)送消息一般使用Handler的sendMessage()方法,而發(fā)出去的消息經(jīng)過處理后最終會傳遞到Handler的handlerMessage()方法中。
- MessageQueue:用于存放Message或Runnable對象的消息隊列。它由對應的Looper對象創(chuàng)建,并由Looper對象管理。每個線程中都只會有一個MessageQueue對象。
boolean enqueueMessage(Message msg, long when) 負責將消息添加到隊列中
Message next() 負責取出消息
- Looper:是每個線程中的MessageQueue的管家,循環(huán)不斷地管理MessageQueue接收和分發(fā)Message或Runnable的工作。調(diào)用Looper的loop()方法后,就會進入到一個無限循環(huán)中然后每當發(fā)現(xiàn)MessageQueue中存在一條消息,就會將它取出,并調(diào)用Handler的handlerMessage()方法。每個線程中也只會有一個Looper對象。
使用ThreadLocal來實現(xiàn)Looper的存儲(多線程內(nèi)存共享時,使用ThreadLocal存儲的變量在各線程中不一致)
public static void loop() 進行循環(huán)
loop.png
進行無限循環(huán)