スレッドおよびメッセージングメカニズム
一:スレッドの作成
1.Threadクラスの構築方法で作成します.
2.Runnableインタフェースの作成を実装します.
3.AsyncTaskの使用
注意:
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のインスタンスを内部に宣言します.
三:スレッドメッセージモード
1.Handler+Thread+Messageモード
このモードではスレッドが使用されるので,非同期ロードの効果が見られる.
このとき,非同期ロードが実現され,インタフェースが開かれると,5つのImageViewには図がなく,それぞれのスレッドがダウンロードされてから自動的に図が更新されることが分かる.
2.Handler+ExecutorService+MessageQueueモード
スレッドを開くことができる個数は結局限られていて、私たちはいつも多くのスレッドを開くことができません.携帯電話にとってはなおさらです.
この例はスレッドプールを使用します.AndroidはJavaと同じExecutorService実装を持っているので、それを使います.
スレッドプールの基本思想はやはりオブジェクトプールの思想であり、メモリ空間を開き、多くの(死亡していない)スレッドが格納され、プール内のスレッド実行スケジューリングはプールマネージャによって処理される.スレッドタスクがある場合は、プールから1つを取り出し、実行が完了したらスレッドオブジェクトをプールに戻すことで、スレッドオブジェクトの繰り返し作成によるパフォーマンスオーバーヘッドを回避し、システムのリソースを節約できます.
スレッドプールの情報は、Java&Androidのスレッドプール-ExecutorServiceの次の例を参照してください.固定スレッド数を再利用できるスレッドプールを作成します.
handlerを使いました.post(new Runnable(){更新前段表示はもちろんUIメインスレッドで、executorServices.submit(new Runnable(){もあり、ダウンロードがオンラインスレッドプールのスレッドであることを確認します.
3.Handler+ExecutorService+MessageQueue+キャッシュモード
次は前よりいくつかの改造を行いました.
A:コード全体を一つのクラスにカプセル化する
B:同じ図を同時に複数回ダウンロードする問題を避けるため、ローカルキャッシュを使用している
説明:
finalパラメータとは、関数パラメータがfinalタイプの場合、このパラメータを読み込んで使用できますが、パラメータの値を変更することはできません.参照:Javaキーワードfinal、static使用概要
ここでSoftReferenceを使用するのは、メモリ不足のエラー(OutOfMemoryError)を解決するためです.より詳細には、メモリ最適化の2つのクラス:SoftReferenceとWeakReferenceを参照してください.
前の呼び出し:
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);
}
}
}