1. 程式人生 > >Android開發筆記(四十七)Runnable介面實現多執行緒

Android開發筆記(四十七)Runnable介面實現多執行緒

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方法
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開發筆記的完整目錄