android:handler
6513 ワード
Handlerの定義:
主にサブスレッドから送信データを受け取り、このデータをメインスレッドに合わせてUIを更新する.解釈:アプリケーションが起動すると、Androidはまずメインスレッド(つまりUIスレッド)を開きます.メインスレッドは管理インタフェースのUIコントロールであり、イベントの配布を行います.例えば、Buttonをクリックすると、AndroidはイベントをButtonに配布し、操作に応答します.インターネットでデータを読み取る、あるいはローカルの大きなファイルを読み取るなど、時間のかかる操作が必要な場合は、これらの操作をメインスレッドに置くことはできません.メインスレッドに置くと、インタフェースに偽死現象が発生し、5秒も完成していない場合は、Androidシステムのエラーメッセージ「強制閉鎖」が表示されます.この場合、サブスレッドはUIの更新に関わるため、Androidメインスレッドはスレッドが安全ではない、すなわち、更新UIはメインスレッドでしか更新できず、サブスレッドでの操作は危険である.この時、Handlerが現れました.この複雑な問題を解決するために、Handlerはメインスレッド(UIスレッド)で実行されるため、サブスレッドとMessageオブジェクトを介してデータを伝達することができる.このとき、Handlerはサブスレッドからの(サブスレッドはsedMessage()メソッドで弟を伝達する)Messageオブジェクトを受け入れ、これらのメッセージをメインスレッドキューに入れる.メインスレッドに合わせてUIを更新します.
Handlerのいくつかの特徴
handlerはMessageオブジェクトとRunnableオブジェクトをプライマリスレッドに配布できます.各Handlerインスタンスは、作成されたスレッドにバインドされます(通常はプライマリスレッドにあります).これには、(1):メッセージのスケジュールまたはRunnableがプライマリスレッドのどこかで実行されるか、(2)異なるスレッドにおいてHandlerでメッセージを配信するためのいくつかの方法post(Runnable)postAtTime(Runnable,long)postDelayed(Runnable long)sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message,long)sendMessageDelayed(Message,long)以上のpostクラスメソッドでは、Runnableオブジェクトをプライマリスレッドキューに配置することができ、sendMessageクラスメソッドでは、データ付きMessageオブジェクトをキューに配置し、更新を待つことができます.
Windowsプログラミングに詳しい方は、Windowsプログラムがメッセージ駆動であり、グローバルなメッセージループシステムがあることを知っているかもしれません.Androidアプリケーションもメッセージドライバであり、メッセージループメカニズムを提供する必要があります.実際、グーグルはWindowsのメッセージサイクルメカニズムを参考にしており、Androidシステムでもメッセージサイクルメカニズムを実現している.AndroidはLooper、Handlerによってメッセージループメカニズムを実現し、Androidメッセージループはスレッド向けである(各スレッドには独自のメッセージキューとメッセージループがある).Androidメッセージ処理システムの原理を詳しく紹介します.
AndroidシステムではLooperがスレッドのメッセージキューとメッセージループを管理しています.具体的にはLooperのソースコードを参照してください.Loopを通ることができます.myLooper()は、現在のスレッドのLooperオブジェクトを得る、Loop.getMainLooper()は、現在のプロセスのメインスレッドのLooperオブジェクトを取得できます.
前述したAndroidシステムのメッセージキューとメッセージループはいずれも特定のスレッドに対して行われており、1つのスレッドは1つのメッセージキューと1つのメッセージループ(Looper)が存在してもよく、特定のスレッドのメッセージは本スレッドにしか配布できず、スレッド間、プロセス間通信ができない.しかし、作成するワークスレッドのデフォルトはメッセージループとメッセージキューがないため、メッセージキューとメッセージループをスレッドに持たせるには、まずLooperをスレッドで呼び出す必要がある.prepare()はメッセージキューを作成し、Looperを呼び出します.loop()はメッセージループに入ります.次の例を示します.
これにより、スレッドにはメッセージ処理メカニズムがあり、Handlerでメッセージ処理を行います.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
ActivityはUIスレッドで、プライマリスレッドで実行され、Androidシステムは起動時にActivityのメッセージキューとメッセージループ(Looper)を作成します.詳細はActivity Threadを参照してください.JAvaファイル.
Handlerの役割は、メッセージを特定の(Looper)メッセージキューに追加し、そのメッセージキュー内のメッセージを配布し、処理することである.Handlerを作成するときにLooperオブジェクトを指定し、指定しない場合は現在のスレッドのLooperを使用して作成します.詳細はLooperのソースコードを参照してください.
Activity、Looper、Handlerの関係を下図に示します.
1つのActivityでは、複数の作業スレッドまたは他のコンポーネントを作成できます.これらのスレッドまたはコンポーネントがActivityのプライマリ・スレッド・メッセージ・キューにメッセージを入れると、メッセージはプライマリ・スレッドで処理されます.メインスレッドは一般的にインタフェースの更新操作を担当し、Androidシステムのwegetはスレッドが安全ではないため、この方式はAndroidインタフェースの更新をうまく実現することができる.Androidシステムではこの方式が広く使われている.
では、別のスレッドはどのようにしてメインスレッドのメッセージキューにメッセージを入れますか?答えはHandleオブジェクトによって,HandlerオブジェクトがメインスレッドのLooperで作成される限り,HandlerのsendMessageなどのインタフェースを呼び出すと,メッセージをキューに入れるのはすべてメインスレッドに入れるメッセージキューである.handlerのhandleMessageインタフェースは、Handlerマスタースレッドで呼び出され、メッセージが処理されます.
スレッド同期の問題については、次の例を参照してHandlerオブジェクトのスレッドモデルを理解してください.
1、まずMyHandlerプロジェクトを作成する.
2、MyHandler.JAvaには、次のコードが追加されています.
package com.simon;
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.os.Handler;
public class MyHandler extends Activity {
static final String TAG = "Handler";
Handler h = new Handler(){
public void handleMessage (Message msg)
{
switch(msg.what)
{
case HANDLER_TEST:
Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "
");
break;
}
}
};
static final int HANDLER_TEST = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "
");
new myThread().start();
setContentView(R.layout.main);
}
class myThread extends Thread
{
public void run()
{
Message msg = new Message();
msg.what = HANDLER_TEST;
h.sendMessage(msg);
Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "
");
}
}
}
この例では,主に印刷を行い,この処理機構の各モジュールのスレッド状況を示す.以下は私の機械の運行結果です.
09-10 23:40:51.478: DEBUG/Handler(302): The main thread id = 1
09-10 23:40:51.569: DEBUG/Handler(302): The worker thread id = 8
09-10 23:40:52.128: DEBUG/Handler(302): The handler thread id = 1
メッセージ処理はプライマリ・スレッドで処理され、メッセージ処理関数では、リフレッシュ・インタフェースを含むプライマリ・スレッド内の任意のリソースを安全に呼び出すことができることがわかります.ワークスレッドとプライマリスレッドは異なるスレッドで実行されるため、この2つのスレッド間の競合関係に注意する必要があります.
前の例では、ワークスレッドでプライマリスレッドhandlerオブジェクトにアクセスし、handlerを呼び出すオブジェクトでメッセージキューにメッセージを追加したことに気づくかもしれません.この過程でメッセージキューのデータが一致しないという問題は発生しませんか?答えはhandlerオブジェクトは問題ありません.handlerオブジェクトが管理するLooperオブジェクトはスレッドが安全で、メッセージキューへの参加やキューからのメッセージの読み出しに同期オブジェクトが保護されています.具体的にはLooperを参照してください.JAvaファイル.前の例ではhandlerオブジェクトは変更されていないので、handlerオブジェクトにデータが一致しないという問題は起こり得ません.
上記の分析から、以下の結論を得ることができます.
1、ワークスレッドでインタフェースをリフレッシュする場合は、handlerオブジェクトを使用して実装することを推奨します.
2、作業スレッドとメインスレッドの競合関係に注意してください.handlerオブジェクトは、プライマリ・スレッドでの構築が完了し(作業スレッドを起動してから変更しないでください.そうしないとデータが一致しません)、作業スレッドで送信メッセージSendMessageなどのインタフェースを安心して呼び出すことができます.
3、2に記載のhanlderオブジェクトを除く任意のプライマリスレッドのメンバー変数がワークスレッドで呼び出される場合、スレッド同期の問題を注意深く考慮する.必要に応じて同期オブジェクトを追加して変数を保護します.
4.handlerオブジェクトのhandleMessageインタフェースがメインスレッドで呼び出されます.この関数では、プライマリ・スレッド内の任意の変数と関数を安心して呼び出し、UIの更新を完了できます.
5、Androidの多くのAPIもHandlerというスレッド特性を利用して、コールバック関数の変種として、呼び出し者に通知している.これにより、Androidフレームワークは、スレッド同期の問題を心配することなく、呼び出し元のスレッドメッセージキューにメッセージを送信することができます.