Android開發筆記(四十七)Runnable介面實現多執行緒
阿新 • • 發佈:2019-01-30
Runnable概述
Runnable介面可宣告一連串的事務,常用於多執行緒處理。但是實現Runnable介面並不意味著開啟了一個新執行緒,只是定義了接下來要做的事情,至於說這些事情要在主執行緒處理,還是在分執行緒處理,那得看我們在哪裡執行Runnable例項。如果在Handler或者View中啟動Runnable,那麼Runnable事務便運行於UI執行緒;如果在Thread中啟動Runnable,那麼Runnable事務便運行於非UI執行緒。實現Runnable介面只需重寫run函式,該函式內部放的就是需要Runnable處理的事務。run方法無需顯式呼叫,在啟動Runnable例項時便會呼叫物件的run方法。
實現Runnable介面相對於繼承Thread類來說,有以下好處:
1、Runnable介面實質是共享程式碼,類似於函式呼叫,但又比函式呼叫靈活,因為Runnable可選擇實際呼叫的時機,而不必像函式呼叫那樣還得等待呼叫結束;
2、可以避免Java單繼承方式的侷限。如果一個新類繼承了Thread類,就不能再繼承別的類。但是Runnable只是介面,所以新類可以繼承別的類,同時實現Runnable介面。
啟動Runnable
Handler常用的post方法有下面幾種:
post : 立即啟動Runnable
postDelayed : 延遲指定時間間隔後啟動Runnable
postAtTime : 在指定時間啟動Runnable
removeCallbacks : 回收/移除指定的Runnable
二、使用View類的post方法
View類也提供了post和postDelayed兩個方法,控制元件或檢視可以直接呼叫自身的post方法,無需另外宣告Handler物件。檢視View的post原始碼,會發現其內部就是呼叫自身Handler例項的post方法。
三、利用Thread類構造Runnable的新執行緒
Thread方式啟動Runnable,也只需兩個步驟,第一步使用Runnable物件構造一個Thread例項,第二步啟動這個Thread例項,程式碼如下:
Thread runTread = new Thread(mRun); runTread.start();
完整的示例程式碼如下:
import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private Handler mHandler = new Handler(); private TextView tv_runnable; private int mCount = 0; private int mOffset = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn_handler = (Button) findViewById(R.id.btn_handler); Button btn_view = (Button) findViewById(R.id.btn_view); Button btn_thread = (Button) findViewById(R.id.btn_thread); btn_handler.setOnClickListener(this); btn_view.setOnClickListener(this); btn_thread.setOnClickListener(this); tv_runnable = (TextView) findViewById(R.id.tv_runnable); } private Runnable mRun = new Runnable() { @Override public void run() { if (mCount < 20) { tv_runnable.scrollBy(-mOffset, -mOffset); //Thread方式啟動Runnable時不能呼叫invalidate方法 //tv_runnable.invalidate(); tv_runnable.postInvalidate(); mHandler.postDelayed(this, 200); mCount++; } } }; @Override public void onClick(View v) { mCount = 0; if (v.getId() == R.id.btn_handler) { mOffset = 10; mHandler.postDelayed(mRun, 500); } else if (v.getId() == R.id.btn_view) { mOffset = 10; tv_runnable.postDelayed(mRun, 500); } else if (v.getId() == R.id.btn_thread) { mOffset = -10; Thread runTread = new Thread(mRun); runTread.start(); } } }
Runnable的適用場景
實際開發中,Runnable一般會延遲一段時間後啟動,這個特性可用於四個方面:1、有些事情需要在Activity頁面顯示出來後才能做,比如廣播接收器一般在onStart或者onResume中註冊,所以onCreate方法中若想傳送廣播後就能接收廣播,那得延遲一點時間。
2、頁面上有些事務不能讓使用者久等,所以如果事務本身無法設定超時時間的話,就得使用Runnable在事務超時後強行結束該事務。
3、在Runnable內部postDelayed自身,並持續post若干週期重新整理檢視,可實現動畫效果。該功能的例子見《Android開發筆記(十四)圓弧進度動畫》
4、有些監聽器如果沒有得到合適的結果,就要持續監聽,直到出現合適的結果為止。該功能的例子見《Android開發筆記(四十六)手機相關事件》
檢視重新整理中的post方法
下列方法用於重新整理View自身檢視:invalidate : 在UI執行緒中重新整理檢視
postInvalidate : 在非UI執行緒中重新整理檢視
postInvalidateDelayed : 在非UI執行緒中延遲若干時間後重新整理檢視
為避免誤解,這裡對invalidate和postInvalidate的區別做進一步的說明:
1、invalidate只能在UI執行緒中呼叫,所以如果在Thread方式中呼叫invalidate就會丟擲異常;postInvalidate可在Thread方式中呼叫,但並不是不能在UI執行緒中呼叫,實際上postInvalidate既可在UI執行緒中呼叫,也可在非UI執行緒中呼叫;
2、invalidate只能立即重新整理檢視,而post方式還有postInvalidateDelayed方法可以延遲一段時間,檢視postInvalidate的原始碼,會發現postInvalidate其實就是呼叫延遲0秒的postInvalidateDelayed;
從上面可以看出,invalidate的適用面較窄,而postInvalidate適用面較廣,當然還是postInvalidateDelayed用起來更靈活些。
點此檢視Android開發筆記的完整目錄