前兩天 ,看一個demo 發現了裡面有使用HandlerThread。心生好奇,自己到網上看了 看資料,又翻了一下開發文檔。寫出來 抛磚引玉。
HandlerThread不能更新ui
我第一次看見handlerThread時候,因為,他帶着Handler還以為是更新UI的,其實不是的。在驗證的過程中,我還發現了 我以前的一個知識點錯誤(受一篇博文的引導。。。)。以後,任何東西 還是自己驗證一下比較好。
首先,我先來說HandlerThread是啥。通俗的來說就是一個子線程具有了 looper和handler這樣的機制 ,當這個子線程建立了handler的時候,别的線程可以通過handler來發送資訊,并且可以在這個handler裡面執行耗時的操作,除了不能更新ui,這樣的機制,我們自己也可以通過
Looper.prepare();
Handler inner = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("内部handler 裡面" + Thread.currentThread().getName() + " ");
Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
textView.setText(">>>>>>>>");
}
};
inner.sendEmptyMessage(1);
Looper.loop();
不過,這樣多費勁是吧。是以 谷歌給我們造了HandlerThread這個類 就是完成這樣作用的。
如果,你運作以上代碼會抛出異常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
這個就是隻有主線程才能更新view。
看上面代碼,我們在handleMessage(Message msg)這個方法裡面 想更新 textView.setText(">>>>>>>>")。是以報錯 了。這個首先說明了子線程聯系的handler是不能更新線程的。
不過,如果 我們把textView.setText()注釋的話。
Looper.prepare();
Handler inner = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("内部handler 裡面" + Thread.currentThread().getName() + " ");
Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
//textView.setText(">>>>>>>>");
}
};
inner.sendEmptyMessage(1);
Looper.loop();
運作 你會發現toast彈出了!!!!
這是怎麼回事了?首先,我們看toast的show方法
看我劃紅線的地方。service服務。我們看一下 getService()方法
我們可以看見 傳回了 INotificationManager對象的靜态執行個體。那麼這個INotificationManager是什麼呢。看上面的英文注釋。翻譯一下的意思是:
下面的所有内容都是與通知服務的互動,通知服務處理這些系統範圍内的正确排序。
這個呢 我們可以發現通知服務,其實到這裡呢,應該有人就明白了。Android 有一個專門的背景服務Service 來進行Toast 等一些通知的顯示 彈出。 而我們知道在Service裡面 我們是可以彈出Toast的。是以 ,這個說明了 一些通知資訊的顯示,都是由背景的 Service來承擔的。我們在前台 隻是一個通知作用
上面略微跑題
接下來 我們來說HandlerThread
HandlerThread handlerThread = new HandlerThread("first");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper(),new HandlerCallback()) {
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
textView.setText(">>>>>>");
}
};
如果,我們這麼用代碼的會和上面抛出一樣的錯誤。因為這段代碼和上面那段代碼的作用是相同的。是以HandlerThread是不能更新UI的
正确使用是 我們仍然需要主線程的handler來更新ui,HandlerThread來接受其他子線程發送過來的消息,這些消息的處理不需要更新ui
//這個是主線程Handler 執行個體 還有一個知識點就是如果new Handler時候沒有給她傳遞looper的話,他會預設的去目前運作線程的looper進行聯系
MainHandler mainHandler = new MainHandler(this);
HandlerThread handlerThread = new HandlerThread("first");
handlerThread.start();
//這是子線程聯系的handler 傳入了handlerThread的looper
final Handler childHandler = new Handler(handlerThread.getLooper()) {
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("子線程 的handler 接受到資料 現在 将資料發送給 主線程 執行 "+Thread.currentThread().getName());
//發送消息給 主線程的handler 讓她更新ui
Message.obtain(mainHandler, 1, ++count).sendToTarget();
}
};
// 開啟一個子線程 調用childHandler 發送資訊
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子線程發送資料 " + Thread.currentThread().getName());
//發送給handlerThread的handler
childHandler.sendEmptyMessage(1);
}
}).start();
}
});
//這個是主線程的Handler 定義成這種形式,可以防止記憶體洩露
static class MainHandler extends Handler{
private WeakReference<MainActivity> mainActivityWeakReference;
MainHandler(MainActivity mainActivity) {
this.mainActivityWeakReference =new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mainActivityWeakReference.get().textView.setText(msg.obj+"");
}
}
在上面我們可以看見,我們開啟了一個子線程,在子線程裡面調用了 HandlerThread的childHandler發送資訊,childHandler接受到消息,再調用主線程的mainHandler重新整理ui。
通過上面的例子來看 ,大家肯定感覺 這不是多此一舉麼。一開始 我也是這麼想的,後來 我仔細思考,這個HandlerThread 其實可以應用于這些情況下面:
- 比如,你同時有好幾個子線程,你需要得到他們運算時候發出的資訊,以前使用接口回調,還得實作的 ,如果 好幾個子線程 豈不是要好幾個接口。
- 比如 還有 一個子線程運算完 你想知道結果,也可以通過這個來實作。以前你使用callable,接口的話 也還是 比較複雜一點的。
小提示
這個handler的構造方法中的callback 官方是這麼說的
在執行個體化處理程式時可以使用回調接口,以避免必須實作自己的處理程式子類。
簡而言之呢,就是 你可以實作這個回調接口,這樣你在執行個體化handler時候,就不要重寫handleMessag()這個方法了。
//在執行個體化處理程式時可以使用回調接口,以避免必須實作自己的處理程式子類。
private class HandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
System.out.println("我是handler Callback " + Thread.currentThread().getName());
//傳回true意味着即使子類重寫了handlerMessage方法 也不會被調用。
//傳回false 如果有子類的話,會調用handlerMessager方法
return true;
}
}
callback 已經實作了 handler方法
還有 調用HandlerThread的handler.post(Runnable) 也是在 子線程 運作的。
貼上全部代碼
package com.example.handlerthread;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private Button button;
MainHandler mainHandler = new MainHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
button = findViewById(R.id.button);
HandlerThread handlerThread = new HandlerThread("first");
handlerThread.start();
final Handler childHandler = new Handler(handlerThread.getLooper(), new HandlerCallback()) {
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("子線程 的handler 接受到資料 現在 将資料發送給 主線程 執行 " + Thread.currentThread().getName());
Message.obtain(mainHandler, 1, ++count).sendToTarget();
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子線程發送資料 " + Thread.currentThread().getName());
childHandler.sendEmptyMessage(1);
}
}).start();
}
});
}
//這個是主線程的Handler 定義成這種形式,可以防止記憶體洩露
static class MainHandler extends Handler {
private WeakReference<MainActivity> mainActivityWeakReference;
MainHandler(MainActivity mainActivity) {
this.mainActivityWeakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mainActivityWeakReference.get().textView.setText(msg.obj + "");
}
}
//在執行個體化處理程式時可以使用回調接口,以避免必須實作自己的處理程式子類。
private class HandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
System.out.println("我是handler Callback " + Thread.currentThread().getName());
return true;
}
}
}