Android 系統中,應用在運行時是一個獨立的進程,可是每一個進程中能夠包含多個線程提升運行效率。在多線程開發中,有一個很重要的原則:不能在子線程中更新 UI 。java
Only the original thread that created a view hierarchy can touch its views.git
爲解決這個問題,目前有多重方案實現子線程和主線程(UI 線程)之間的通訊。github
在一些簡單代碼邏輯中,也許可以很清晰的辨別出運行在子線程或主線程中。一般在複雜的類關係依賴、函數嵌套調用中,可能須要花費很大精力去閱讀代碼以後去判斷。不過,巧法子也是有的,一行代碼解決。多線程
Log.d("TAG","test");
複製代碼
日誌內容中,2368-2393
表示是在子線程中輸出日誌。ide
11-16 01:08:31.584 2368-2393/com.flueky.demo D/TAG: test函數
其中 2368 表示 PID
指進程id, 2393 表示 TID
指線程id 。若是 TID
也是 2368 ,則表示日誌輸出在主線程中。oop
可能也有人聽過 UID
,應用第一次安裝在設備上時,系統會分配一個序號給應用,做爲其惟一標識。UID
在覆蓋安裝時不會變化,卸載安裝時系統會從新分配一個。post
下面是在代碼中獲取三個 id 的方式。ui
// 獲取 tid
Process.myTid()
// 獲取 pid
Process.myPid()
// 獲取 uid
Process.myUid()
複製代碼
遇到須要在子線程中更新 UI 操做時,能夠經過下面的這些方式解決。this
子線程代碼運行在 Activity 或 Fragment 中,能獲取到任意 view 的引用時,可使用此方式將須要實現的代碼放在主線程中運行。
// post 方法在子線程中調用
textView.post(new Runnable() {
@Override
public void run() {
// 此處代碼會在 UI 線程執行
}
});
複製代碼
若是可以直接獲取到 Activity 實例,使用 runOnUiThread 方法。
// runOnUiThread 方法在子線程中調用
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// 此處代碼會在 UI 線程執行
}
});
複製代碼
使用 Handler 比較講究,由於須要考慮到 Handler 實例初始化的位置。
// post 方法在子線程中調用
handler.post(new Runnable() {
@Override
public void run() {
// handler 在主線程中初始化時,此處代碼在主線程中執行
// handler 在子線程中初始化事,此處代碼在子線程中執行
}
});
複製代碼
以上說法其實不夠嚴謹,存在下面的狀況,初始化 handler 實例時傳入 Looper.getMainLooper() ,則 handler.post 也在主線程中執行。
// 下面的代碼在子線程中執行
Looper.prepare();
handler = new Handler(Looper.getMainLooper());
Looper.loop();
複製代碼
EventBus
出自 greenrobot ,經過訂閱的方式,告知函數運行在哪一個線程中。爲使訂閱函數在主線程中執行,使用註解 MAIN 或 MAIN_ORDERED 。
/** * eventbus 簡單示例 */
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/** * 訂閱函數, * ThreadMode.MAIN 表示在主線程中運行,可能會阻塞子線程。 * ThreadMode.MAIN_ORDERED 表示在主線程中運行,不會阻塞子線程。 */
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(Object event) {
if(event instanceof Runnable)
((Runnable)event).run();
}
@Override
protected void onStart() {
super.onStart();
// 註冊 eventbus 監聽
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
// 註銷 eventbus 監聽
EventBus.getDefault().unregister(this);
}
}
// 在子線程中發送消息
EventBus.getDefault().post(new Runnable() {
@Override
public void run() {
// 此處代碼會在 UI 線程執行
}
});
複製代碼
前面四種方式演示瞭如何在子線程中作更新 UI 操做。 AsyncTask
也具有相同用法,可是有點牽強,由於只有 execute 方法在主線程中執行,onPostExecute 纔會在主線程中調用。因爲 onPostExecute 能夠接收到子線程傳遞的任意類型的對象數據,因此 AsyncTask
能夠做爲線程間的數據交互的載體。對此 Handler 和 EventBus 表示不服。
EventBus 如以前所示,能夠將 Runnable 對象換成任意實例。
Handler 也能夠經過 sendMessage 方法發送 Message 對象。其中 Message.obj 用做傳遞對象數據的載體。
建議使用 Message.obtain() 方法複用 Message 實例。
順便提下,BroadcastReceiver
也能夠做爲此類用途,只不過沒有 EventBus 和 Handler 方便。
以爲有用?那打賞一個唄。去打賞
我的主頁已經更新 ,歡迎收藏flueky.github.io/。