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 1を起動し、Handlerクラスのpostメソッドを採用する
Handlerでよく使用されるpostメソッドには、次のようなものがあります.
post:Runnableを直ちに起動する
postDelayed:指定間隔遅延後にRunnableを起動する
postAtTime:指定した時間にRunnableを起動する
removeCallbacks:指定したRunnableの回収/除去
二、Viewクラスを使用するpost方法
ViewクラスにはpostとpostDelayedの2つのメソッドも用意されており、Handlerオブジェクトを別途宣言することなく、コントロールまたはビューで独自のpostメソッドを直接呼び出すことができます.Viewのpostソースコードを表示すると、その内部が自身のHandlerインスタンスを呼び出すpostメソッドであることがわかります.
三、Threadクラスを利用してRunnableの新しいスレッドを構築する
Thread方式でRunnableを起動するには、2つのステップしか必要ありません.最初のステップはRunnableオブジェクトを使用してThreadインスタンスを構築し、2番目のステップはこの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は一般的に時間を遅らせて起動します.この特性は4つの側面で使用できます.
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方法もある.
上から分かるように、invalidateの適用面は狭く、postInvalidateの適用面は広く、もちろんpostInvalidateDelayedの方が柔軟です.
Android開発ノートの完全なディレクトリを確認します