スレッドおよびメッセージングメカニズム


一:スレッドの作成
 
 
1.Threadクラスの構築方法で作成します.
Thread thread = new Thread(new Runnable() {
	@Override
	public void run() {
		// do something;
	}
});

 
 
2.Runnableインタフェースの作成を実装します.
 
public class PageActivity11 extends Activity implements Runnable{			
@Override			
protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
				Thread thread = new Thread(MainActivity.this);
		thread.start();
	}
			@Override
			public void run() {
		// do something;
	}
}
 
 
 
3.AsyncTaskの使用
 
public class GetAddresshAsync extends AsyncTask<Void, Integer, Void> {
	@Override
	protected Void doInBackground(Void... params) {
	// do something;
	}
	@Override
	protected void onPostExecute(Void result) {
	//      ,   UI
	}
}
 
 
 
注意:
A:メインスレッドはUI関連イベントを管理し、ユーザーが作成したサブスレッドはUIコンポーネントを操作できない.
 
B:t.interrupt()は、実行中のスレッドを中断することはなく、スレッドのフラグビットをtrueに設定するだけです.しかし、スレッドがsleep()、join()、wait()メソッドを呼び出すときにスレッドが中断されると、これらのメソッドはInterruptedExceptionを放出し、catchブロックでこの異常をキャプチャすると、スレッドの中断フラグビットがfalseに設定されているため、このcatchブロックでt.isInterrupted()、Thread.interrupted()は常にfalseであり、t.isInterruptedとThread.interrupted()の違いはAPIで明らかになったことだ.interrupted()現在の割り込みフラグがtrueの場合、調整が完了すると割り込みフラグビットがfalseに設定されます.中止スレッドはrun()メソッドでフラグを設定できます.sleep()、join()、wait()メソッドを使用する場合は、catchブロックでbreakを使用してrun()メソッドから飛び出します.
 
二:メッセージングメカニズム(Message、MessageQueue、Looper、Handler)
 
1.Message:メッセージオブジェクト、その名の通りメッセージ情報を記録するクラス.このクラスにはいくつかの重要なフィールドがあります.
arg 1とarg 2:伝達する必要がある整数値を格納するために2つのフィールドを使用できます.サービスでは、サービスIDを格納するために使用できます.
obj:このフィールドはObjectタイプで、メッセージの受信者に複数のフィールドを渡すことができます.
what:このフィールドはメッセージのフラグと言えますが、メッセージ処理では、Buttonイベントを処理するときにswitch(v.getId()でどのボタンをクリックしたかを判断するのと同じように、このフィールドの異なる値に基づいて異なる処理を行うことができます.
Messageを使用する場合、new Message()でMessageインスタンスを作成できますが、AndroidではMessageを使用することをお勧めします.Obtain()またはHandler.obtainMessage()は、Messageオブジェクトを取得します.これは、必ずしも新しいインスタンスを直接作成するわけではありません.メッセージ・プールから使用可能なMessageインスタンスがあるかどうかを確認し、存在する場合は、このインスタンスを直接取り出して返します.逆に、メッセージ・プールに使用可能なMessageインスタンスがない場合は、指定されたパラメータnewに基づいて新しいMessageオブジェクトが作成されます.ソースコードを解析すると、Androidシステムはデフォルトでメッセージプールに10個のMessageオブジェクトをインスタンス化していることがわかります.
 
2.MessageQueue:Messageオブジェクトのデータ構造を格納するためのメッセージキューであり、「先進先出」の原則に従ってメッセージを格納する.保存は実際の意味での保存ではなく、Messageオブジェクトをチェーンテーブルで直列に接続します.MessageQueueオブジェクトは、私たちが自分で作成する必要はありません.Looperオブジェクトが管理しています.スレッドは最大1つのMessageQueueしか持てません.私たちはLooperを通じてmyQueue()は、現在のスレッドのMessageQueueを取得します.
 
3.Looper:MessageQueueの管理者で、1つのスレッドでLooperオブジェクトが存在する場合、必ずMessageQueueオブジェクトが存在し、1つのLooperオブジェクトと1つのMessageQueueオブジェクトのみが存在する.我々のスレッドにLooperオブジェクトが存在する場合、Looper.myLooper()を取得し、Looper.を通じてgetMainLooper()は、現在のアプリケーションシステムのメインスレッドのLooperオブジェクトを取得します.ここで注意すべき点は、Looperオブジェクトがアプリケーションのメインスレッドにある場合、Looper.myLooper()とLooper.getMainLooper()は同じオブジェクトを取得します.同じスレッドの下のHandlerはLooperオブジェクトを共有します.
 
4.Handler:メッセージの処理者.HandlerオブジェクトによってMessageオブジェクトをカプセル化し、sendMessage(msg)によってMessageオブジェクトをその存在するMessage Queueに追加することができます.Message QueueがMessageにループすると、Messageオブジェクトに対応するhandlerオブジェクトのhandleMessage()メソッドが呼び出されて処理されます.メッセージはhandleMessage()メソッドで処理されるため、Handlerから継承されたクラスを記述し、handleMessage()で必要な操作を処理する必要があります.1つのスレッドに複数のHandlerを持つことができ、非スレッドにHandlerを作成する場合は、そのスレッドにLooper(Looper.prepare()を作成する必要があります.)メッセージループ(Looper.myLooper()を確立する.loop();).メインスレッドで実行されるHandler(Looper.getMainLooper(){...})を、Handlerの作成時にパラメータで入力することもできます.
なお、Activity finish()の場合はHandlerを呼び出す必要がある.removeCallbacksAndMessages(null)メッセージキューを空にします.そうしないとdelayメッセージが処理されていない場合、ActivityはonDestory()を呼び出しません.ただし、スレッドにメッセージが送信され続ける場合は、handleMessage処理メッセージが実行されます.したがって、非同期スレッドメッセージが処理する必要があり、handleMessageでNullPointExceptionまたはtryを使用すると判断する.catchブロック.
Handler Leakについて:AndroidではHandlerクラスは静的であるべきで、そうでなければ漏れが発生する可能性があります.アプリケーションスレッドのMessageQueueにキューされたMessageオブジェクトは、ターゲットHandlerも保持されます.Handlerが内部クラスである場合(注:匿名でも匿名でもない場合、匿名は一般的な使い方です)、その外部クラスは保持されます(なぜかについては、Javaネストクラスに関する説明を参照してください).外部クラスの漏洩を回避するために、外部クラスインスタンスに対するHandlerサブクラスの自動参照を回避するために、外部クラスオブジェクトに対するHandlerオブジェクトのWeakReferenceを内部に持つ静的内部クラスを宣言します.HandlerLeakの詳細な説明です.
A:キュー内のMessageオブジェクトのHandlerに対する所持による漏洩:Handlerを呼び出す.removeCallbacksAndMessages(null)メッセージキューを空にします.
B:ActivityやServiceなどの外部クラスのインスタンスに対するHandlerオブジェクトの強い参照:Handlerの実装クラスは静的内部クラスを採用し、外部クラスへの強い参照を回避し、外部クラスへのWeakReferenceのインスタンスを内部に宣言します.
 
static class MyHandler extends Handler {
	// WeakReference to the outer class's instance.
	private WeakReference<HandlerActivity> mOuter;

	public MyHandler(HandlerActivity activity) {
		mOuter = new WeakReference<HandlerActivity>(activity);
	}
	@Override
	public void handleMessage(Message msg) {
		HandlerActivity outer = mOuter.get();
		if (outer != null) {
			//    mOuter.classfunction          。
			Log.i("Handler", "handleMessage");
		}
	}
}

private final MyHander mHandler = new MyHandler();
 
 
 
三:スレッドメッセージモード
 
1.Handler+Thread+Messageモード
 
このモードではスレッドが使用されるので,非同期ロードの効果が見られる.
public class TestActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		loadImage2("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
		loadImage2("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
		loadImage2("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
		loadImage2("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id. imageView4);
		loadImage2("http://images.cnblogs.com/logo_small.gif", R.id. imageView5);
	}

	final Handler handler2 = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			((ImageView) TestActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable) msg.obj);
		}
	};

	//   handler+Thread           
	private void loadImage2(final String url, final int id) {
		Thread thread = new Thread() {
			@Override
			public void run() {
				Drawable drawable = null;
				try {
					drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
				} catch (IOException e) {
					Log.d("test", e.getMessage());
				}

				//       
				SystemClock.sleep(2000);
				Message message = handler2.obtainMessage();
				message.arg1 = id;
				message.obj = drawable;
				handler2.sendMessage(message);
			}
		};
		thread.start();
		thread = null;
	}
}

このとき,非同期ロードが実現され,インタフェースが開かれると,5つのImageViewには図がなく,それぞれのスレッドがダウンロードされてから自動的に図が更新されることが分かる.
 
2.Handler+ExecutorService+MessageQueueモード
スレッドを開くことができる個数は結局限られていて、私たちはいつも多くのスレッドを開くことができません.携帯電話にとってはなおさらです.
この例はスレッドプールを使用します.AndroidはJavaと同じExecutorService実装を持っているので、それを使います.
スレッドプールの基本思想はやはりオブジェクトプールの思想であり、メモリ空間を開き、多くの(死亡していない)スレッドが格納され、プール内のスレッド実行スケジューリングはプールマネージャによって処理される.スレッドタスクがある場合は、プールから1つを取り出し、実行が完了したらスレッドオブジェクトをプールに戻すことで、スレッドオブジェクトの繰り返し作成によるパフォーマンスオーバーヘッドを回避し、システムのリソースを節約できます.
スレッドプールの情報は、Java&Androidのスレッドプール-ExecutorServiceの次の例を参照してください.固定スレッド数を再利用できるスレッドプールを作成します.
public class TestActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
		loadImage3("http://www.chinatelecom.com.cn/images/logo_new.gif",
				R.id.imageView2);
		loadImage3("http://cache.soso.com/30d/img/web/logo.gif",
					R.id.imageView3);
		loadImage3("http://csdnimg.cn/www/images/csdnindex_logo.gif",
					R.id.imageView4);
		loadImage3("http://images.cnblogs.com/logo_small.gif", R.id. imageView5);
	}

	private Handler handler = new Handler();

	private ExecutorService executorService = Executors.newFixedThreadPool(5);

	//            
	private void loadImage3(final String url, final int id) {
		executorService.submit(new Runnable() {
			public void run() {
				try {
					final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
					//       
					SystemClock.sleep(2000);
					handler.post(new Runnable() {
						public void run() {
							((ImageView) TestActivity.this.findViewById(id))
										.setImageDrawable(drawable);
						}
					});
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		});
	}
}

 
handlerを使いました.post(new Runnable(){更新前段表示はもちろんUIメインスレッドで、executorServices.submit(new Runnable(){もあり、ダウンロードがオンラインスレッドプールのスレッドであることを確認します.
 
3.Handler+ExecutorService+MessageQueue+キャッシュモード
次は前よりいくつかの改造を行いました.
A:コード全体を一つのクラスにカプセル化する
B:同じ図を同時に複数回ダウンロードする問題を避けるため、ローカルキャッシュを使用している
public class AsyncImageLoader3 {
	//       ,        (            ,             ,   ListView     )
	public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
	private ExecutorService executorService = Executors.newFixedThreadPool(5); //            
	private final Handler handler = new Handler();
	/**
	 * 
	* @param imageUrl
	 *             url  
	 * @param callback
	 *                <a
	 *           href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\""
	 *           target="\"_blank\"">@return</a>           ,       null
	 */
	public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) {
		//               
		if (imageCache.containsKey(imageUrl)) {
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			if (softReference.get() != null) {
				return softReference.get();
			}
		}
		//        ,         ,             
		executorService.submit(new Runnable() {
			public void run() {
				try {
					final Drawable drawable = loadImageFromUrl(imageUrl);
					imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
					handler.post(new Runnable() {
						public void run() {
							callback.imageLoaded(drawable);
						}
					});
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		});
		return null;
	}

	//          
	protected Drawable loadImageFromUrl(String imageUrl) {
		try {
			//    ,      ,          
			SystemClock.sleep(2000);
			return Drawable.createFromStream(new URL(imageUrl).openStream(),
					"image.png");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	//           
	public interface ImageCallback {
		//                     
		public void imageLoaded(Drawable imageDrawable);
	}
}

 
説明:
finalパラメータとは、関数パラメータがfinalタイプの場合、このパラメータを読み込んで使用できますが、パラメータの値を変更することはできません.参照:Javaキーワードfinal、static使用概要
ここでSoftReferenceを使用するのは、メモリ不足のエラー(OutOfMemoryError)を解決するためです.より詳細には、メモリ最適化の2つのクラス:SoftReferenceとWeakReferenceを参照してください.
前の呼び出し:
public class TestActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
		loadImage4("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
		loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
		loadImage4("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
		loadImage4("http://images.cnblogs.com/logo_small.gif", R.id. imageView5);
	}
	private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();
	//      ,         ,           ,      
	private void loadImage4(final String url, final int id) {
		//                ,ImageCallback           
		Drawable cacheImage = asyncImageLoader3.loadDrawable(url,
				new AsyncImageLoader3.ImageCallback() {
					//      :       url        
					public void imageLoaded(Drawable imageDrawable) {
						((ImageView) findViewById(id)).setImageDrawable(imageDrawable);
					}
				});
		if (cacheImage != null) {
			((ImageView) findViewById(id)).setImageDrawable(cacheImage);
		}
	}
}