源碼解讀 Android 消息機制( Message MessageQueue Handler Looper)中詳細介紹了Looper,handle,Message的使用關係。java
//主線程
mUIHandler = new Handler() {
public void handleMessage(Message msg) {
// 這裏處理消息
}
};
//子線程
class SubThread extends Thread {
public void run() {
//其餘耗時代碼
mHandler.sendMessage(new Message());//發送到主線程處理
}
複製代碼
public Handler mHandler1;
//子線程1 建立looper並循環
class LooperThread1 extends Thread {
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 這裏處理消息
}
};
Looper.loop();
}
//子線程2
class LooperThread2 extends Thread {
public void run() {
//線程2的其餘代碼
mHandler1.sendMessage(Message.obtain());//發送到線程1的MessageQueue中,在線程1中去處理
}
複製代碼
很明顯的一點就是,咱們要在子線程中調用Looper.prepare() 爲一個線程開啓一個消息循環,默認狀況下Android中新誕生的線程是沒有開啓消息循環的。(主線程除外,主線程系統會自動爲其建立Looper對象,開啓消息循環。) Looper對象經過MessageQueue來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。 而後經過Looper.loop() 讓Looper開始工做,從消息隊列裏取消息,處理消息。android
注意:寫在Looper.loop()以後的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()後,loop纔會停止,其後的代碼才能得以運行。web
能夠看到,很是繁瑣,一層套一層看着也不美觀。安全
HandlerThread
就是爲了幫咱們免去寫上面那樣的代碼而生的網絡
官方文檔對它的介紹:app
HandlerThread 是一個包含 Looper 的 Thread,咱們能夠直接使用這個 Looper 建立 Handler異步
HandlerThread,繼承自Thread,本質是Thread
,它與普通Thread的差異就在於,它有個Looper成員變量
。其內部就是經過Thread+Looper
來實現的,說白了HandlerThread就是Android已經封裝好的一個擁有本身looper的線程,咱們能夠利用它執行一些耗時任務。咱們先來看看HandlerThread的使用步驟並提供給你們一個使用案例:ide
HandlerThread handlerThread = new HandlerThread("downloadImage");
複製代碼
參數的做用主要是標記當前線程的名字,能夠任意字符串。函數
//必須先開啓線程
handlerThread.start();
複製代碼
到此,咱們就構建完一個循環線程。那麼咱們怎麼將一個耗時的異步任務投放到HandlerThread線程中去執行呢?接下來看下面步驟:oop
/**
* 該callback運行於子線程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子線程中進行相應的網絡請求
//通知主線程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
複製代碼
構建異步handler
//子線程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
複製代碼
第3步是構建一個能夠用於異步操做的handler,並將前面建立的HandlerThread的Looper對象和Callback接口類做爲參數傳遞給當前的handler,這樣當前的異步handler就擁有了HandlerThread的Looper對象,而其中的handlerMessage方法來處理耗時任務,Looper+Handler+MessageQueue+Thread異步循環機制構建完成。下面咱們來看一個使用案例
public class HandlerThreadActivity extends Activity {
/**
* 圖片地址集合,圖片來自網絡.
*/
private String url[]={
"http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg"
};
private ImageView imageView;
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
LogUtils.e("次數:"+msg.what);
ImageModel model = (ImageModel) msg.obj;
imageView.setImageBitmap(model.bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
imageView= (ImageView) findViewById(R.id.image);
//建立異步HandlerThread
HandlerThread handlerThread = new HandlerThread("downloadImage");
//必須先開啓線程
handlerThread.start();
//子線程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
for(int i=0;i<10;i++){
//每一個1秒去更新圖片
childHandler.sendEmptyMessageDelayed(i,1000*i);
}
}
/**
* 該callback運行於子線程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子線程中進行網絡請求
Bitmap bitmap=downloadUrlBitmap(url[msg.what]);
ImageModel imageModel=new ImageModel();
imageModel.bitmap=bitmap;
imageModel.url=url[msg.what];
Message msg1 = new Message();
msg1.what = msg.what;
msg1.obj =imageModel;
//通知主線程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
private Bitmap downloadUrlBitmap(String urlString) {
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
Bitmap bitmap=null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
bitmap=BitmapFactory.decodeStream(in);
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
}
複製代碼
思路分析:在這個案例中,咱們建立了兩個Handler,一個用於更新UI線程的mUIHandler和一個用於異步下載圖片的childHandler。最終的結果是childHandler會每一個隔1秒鐘經過sendEmptyMessageDelayed方法去通知ChildCallback的回調函數handleMessage方法去下載圖片並告訴mUIHandler去更新UI界面。
HandlerThread 源碼很是簡單,看起來 so easy:
public class HandlerThread extends Thread {
//優先級
int mPriority;
//線程id
int mTid = -1;
//線程的looper
Looper mLooper;
public HandlerThread(String name) {
super(name);
//默認
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//也能夠指定線程的優先級,注意使用的是 android.os.Process 而不是 java.lang.Thread 的優先級!
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 子類須要重寫的方法,在這裏作一些執行前的初始化工做
protected void onLooperPrepared() {
}
//獲取當前線程的 Looper
//若是線程不是正常運行的就返回 null
//若是線程啓動後,Looper 還沒建立,就 wait() 等待 建立 Looper 後 notify
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) { //循環等待
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
//調用 start() 後就會執行的 run()
@Override
public void run() {
mTid = Process.myTid();
//幫咱們建立了 Looepr
Looper.prepare();
synchronized (this) {
//Looper 已經建立,喚醒阻塞在獲取 Looper 的線程
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
//子類可重寫的方法
onLooperPrepared();
//開始循環
Looper.loop();
//調用quit後纔會執行
mTid = -1;
}
//退出
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//安全退出
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
複製代碼
能夠看到,
①HandlerThread 本質仍是個 Thread,建立後別忘了調用 start()。
②在 run() 方法中建立了 Looper,調用 onLooperPrepared 後開啓了循環
③咱們要作的就是在子類中重寫 onLooperPrepared,作一些初始化工做
④在建立 HandlerThread 時能夠指定優先級,注意這裏的參數是 Process.XXX 而不是 Thread.XXX
咱們知道,HandlerThread 所作的就是在新開的子線程中建立了 Looper,那它的使用場景就是 Thread + Looper 使用場景的結合,即:在子線程中執行耗時的、可能有多個任務的操做
。
好比說多個網絡請求操做,或者多文件 I/O
等等
參考連接: