【Android】java.lang.AssertionError use looper thread, must call Looper.prepare() first!いじょうぶんせき
java.lang.AssertionError: use looper thread, must call Looper.prepare() first!
メッセージ処理でLooperクラスのprepare()メソッドを呼び出す必要があります.
次の2つの例のコード:1つはMainActivityであり、1つはそれによって開かれたActivityである.システムのデフォルトではメッセージキューが作成されますが、Activity TwoはMainActivityによって作成され、オープンします.共通のMainActivityのメッセージキューは、Looperを明示的に呼び出す2つの方法でサブスレッドのメッセージを処理する必要はありません.
一方、Activity TwoまたはMainActivityで別のクラス(非コンポーネントクラス)が呼び出され、このクラスにサブスレッドメッセージ更新が含まれている場合、システムがデフォルトで作成したメッセージキューを共有することはできないため、Looperを明示的に呼び出す2つの方法しかなく、サブスレッドのメッセージを処理してUI(例えば、ポップアップトースト)を更新することができない.
たとえば、次の例では、イベント呼び出しのStartThread()メソッドをクリックすることで、MainActivityのスレッドとは異なり、メッセージキューを共有できないスレッドを開くため、ここではnew Threadのrun()メソッドでLooper新規メッセージキューを呼び出す必要がある.new Thread()の前に呼び出された、つまりスレッドがまだ開いていない場合、すべてのコードがMainActivityスレッド空間にあるとしたら、「統合スレッドにメッセージキューを1つしか作成できない」という異常が報告されます.
LooperとHandlerは生まれつきのペアですが、システムはデフォルトでメッセージキューを作成します.Activityや他のコンポーネントでHandlerを使用するときは自分でLooperを呼び出す必要はありません.他のスレッドでは、LooperとHandlerメカニズムを使用するには、LooperのprepareとloopメソッドでHandlerをラップする必要があります.
LooperとHandlerのもう一つの問題
プライマリ・スレッドがオンになると、他のクラスのメソッドを呼び出す必要がありますが、他のクラスのメソッドは時間がかかるため、サブスレッドで完了します.しかし、呼び出し者は、このサードパーティのクラスがいつこの時間のかかるタスクを完了したかを知って、次の操作を決定する必要があります.最初はコンテンツオブザーバーやブロードキャストの形式でグローバルな通知を行うべきだと思い,呼び出し者にタスクが完了したことを伝える.でもこれは重量級すぎて、鶏を殺して牛刀を使う感じです.インライン/スレッド通信の方法をずっと使いたいと思っていますが、結局はもっと簡単です.HandlerとLooperのメカニズムを使用することを考慮したことがなく、**は**HandlerとLooperがペアに属していると感じなければならない.呼び出し者と実行者は異なるスレッドで実行され、メッセージを送っても指定されたメッセージキューに送信できるとは限らない.つまり、呼び出し者がメッセージを送信しても呼び出し者のメッセージループキューに送信できるとは限らない.今日テストしたのは,呼び出し元が呼び出し元の1つのHandlerオブジェクトに与えられる限り,呼び出し元のサブスレッドはこのHandlerを用いて呼び出し元のスレッドにメッセージを送信し,呼び出し元スレッドのメッセージキューに入ることができるということである.
サンプルコード解析
サブスレッドでhandler=new Handler(){handleMessage(){};}を使用する場合.メソッドは、まずLooperを呼び出す必要があります.prepare()メソッドは、handleMessage()メソッドを書き換えなくても、サブスレッドでhandler=new Handler()を使用する限り、初期化文はLooperを呼び出す必要があります.prepare()
プライマリ・スレッドでhandlerを初期化または付与する場合は、Looperを使用する必要はありません.
対応するログ出力は,このときのサブスレッドのHandlerはhandleMessage()メソッドを書き換えていないため,システムhandlerに属する.
Handlerを初期化するときにhandleMessage()メソッドを書き換えると、Handlerは変更され、具体的なエンジニアリングに関連します.
コードは次のとおりです.
ログの出力は次のとおりです.
一方,呼び出し元から渡されたHandlerを用いると,呼び出し元のメッセージキューにメッセージを送信することができ,呼び出し元のLooperはメッセージキューからメッセージを取り出して処理することもできる.この方法は、単純なイン/スレッド間の同期に便利です.
サブスレッドコード:
サブスレッドログ:
メインスレッドコード:
プライマリ・スレッド・ログ
比較ログでは、同じスレッドでは実行されないが、サブスレッドとプライマリスレッドのmHandlerは同じHandlerオブジェクトを指し、サブスレッドがこのHandlerオブジェクトを使用してプライマリスレッドにメッセージを正常に送信し、プライマリスレッドがこのメッセージを受信して処理していることがわかります.
メッセージ処理でLooperクラスのprepare()メソッドを呼び出す必要があります.
次の2つの例のコード:1つはMainActivityであり、1つはそれによって開かれたActivityである.システムのデフォルトではメッセージキューが作成されますが、Activity TwoはMainActivityによって作成され、オープンします.共通のMainActivityのメッセージキューは、Looperを明示的に呼び出す2つの方法でサブスレッドのメッセージを処理する必要はありません.
public class MainActivity extends Activity implements OnClickListener{
public Button bt_click;
public Button bt_click_2;
public Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_click = (Button) findViewById(R.id.bt_click);
bt_click_2 = (Button) findViewById(R.id.bt_click_2);
bt_click.setOnClickListener(this);
bt_click_2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_click:
// Activity
Intent intent = new Intent(MainActivity.this, ActivityTwo.class);
startActivity(intent);
break;
case R.id.bt_click_2:
// , Handler, , Looper
AnotherClass.StartThread(MainActivity.this);
break;
default:
break;
}
}
}
public class ActivityTwo extends Activity implements OnClickListener{
public Button bt_click;
public Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
bt_click = (Button) findViewById(R.id.bt_click);
/*
* Handler MainActivity , Looper
*/
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(ActivityTwo.this, "ActivityTwo: 1", 0).show();
break;
}
}
};
bt_click.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_click:
//
new Thread(){
public void run() {
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
};
}.start();
break;
default:
break;
}
}
}
一方、Activity TwoまたはMainActivityで別のクラス(非コンポーネントクラス)が呼び出され、このクラスにサブスレッドメッセージ更新が含まれている場合、システムがデフォルトで作成したメッセージキューを共有することはできないため、Looperを明示的に呼び出す2つの方法しかなく、サブスレッドのメッセージを処理してUI(例えば、ポップアップトースト)を更新することができない.
たとえば、次の例では、イベント呼び出しのStartThread()メソッドをクリックすることで、MainActivityのスレッドとは異なり、メッセージキューを共有できないスレッドを開くため、ここではnew Threadのrun()メソッドでLooper新規メッセージキューを呼び出す必要がある.new Thread()の前に呼び出された、つまりスレッドがまだ開いていない場合、すべてのコードがMainActivityスレッド空間にあるとしたら、「統合スレッドにメッセージキューを1つしか作成できない」という異常が報告されます.
java.lang.RuntimeException: Only one Looper may be created per thread
public class AnotherClass {
public static Handler handler;
public static void StartThread(final Context context){
new Thread(){
public void run() {
// run
// Looper Handler ,
Looper.prepare();
handler = new Handler(){
// do sth
};
Toast.makeText(context, "AnotherClass : test message queue", 0).show();
Looper.loop();
};
}.start();
}
}
LooperとHandlerは生まれつきのペアですが、システムはデフォルトでメッセージキューを作成します.Activityや他のコンポーネントでHandlerを使用するときは自分でLooperを呼び出す必要はありません.他のスレッドでは、LooperとHandlerメカニズムを使用するには、LooperのprepareとloopメソッドでHandlerをラップする必要があります.
LooperとHandlerのもう一つの問題
プライマリ・スレッドがオンになると、他のクラスのメソッドを呼び出す必要がありますが、他のクラスのメソッドは時間がかかるため、サブスレッドで完了します.しかし、呼び出し者は、このサードパーティのクラスがいつこの時間のかかるタスクを完了したかを知って、次の操作を決定する必要があります.最初はコンテンツオブザーバーやブロードキャストの形式でグローバルな通知を行うべきだと思い,呼び出し者にタスクが完了したことを伝える.でもこれは重量級すぎて、鶏を殺して牛刀を使う感じです.インライン/スレッド通信の方法をずっと使いたいと思っていますが、結局はもっと簡単です.HandlerとLooperのメカニズムを使用することを考慮したことがなく、**は**HandlerとLooperがペアに属していると感じなければならない.呼び出し者と実行者は異なるスレッドで実行され、メッセージを送っても指定されたメッセージキューに送信できるとは限らない.つまり、呼び出し者がメッセージを送信しても呼び出し者のメッセージループキューに送信できるとは限らない.今日テストしたのは,呼び出し元が呼び出し元の1つのHandlerオブジェクトに与えられる限り,呼び出し元のサブスレッドはこのHandlerを用いて呼び出し元のスレッドにメッセージを送信し,呼び出し元スレッドのメッセージキューに入ることができるということである.
サンプルコード解析
サブスレッドでhandler=new Handler(){handleMessage(){};}を使用する場合.メソッドは、まずLooperを呼び出す必要があります.prepare()メソッドは、handleMessage()メソッドを書き換えなくても、サブスレッドでhandler=new Handler()を使用する限り、初期化文はLooperを呼び出す必要があります.prepare()
プライマリ・スレッドでhandlerを初期化または付与する場合は、Looperを使用する必要はありません.
public class ChildThread {
protected static final String TAG = "child-thread";
private Handler handler; // = new Handler();
public ChildThread(Handler handler) {
super();
this.handler = new Handler();
Log.i(TAG, "befor assign : " + this.handler.toString());
}
public void doSth(){
new Thread(){
public void run() {
try {
//Looper.prepare();
sleep(2000);
handler.sendEmptyMessage(200);
Log.i(TAG, "AFTER assign : " + handler.toString());
Log.i(TAG, "msg send");
Log.i(TAG, "handler in child thread : " + handler.toString());
Log.i(TAG, "child thread ID : " + Thread.currentThread().getId());
Log.i(TAG, "child thread name : " + Thread.currentThread().getName());
//Looper.loop();
} catch (InterruptedException e) {
Log.e(TAG, "InterruptedException in child thread " + e.toString());
e.printStackTrace();
}
};
}.start();
}
}
対応するログ出力は,このときのサブスレッドのHandlerはhandleMessage()メソッドを書き換えていないため,システムhandlerに属する.
android.os.Handler
01-04 23:13:48.771: I/child-thread(1356): befor assign : Handler (android.os.Handler) {4176b1d8}
01-04 23:13:50.868: I/child-thread(1356): AFTER assign : Handler (android.os.Handler) {4176b1d8}
01-04 23:13:50.868: I/child-thread(1356): msg send
01-04 23:13:50.871: I/child-thread(1356): handler in child thread : Handler (android.os.Handler) {4176b1d8}
01-04 23:13:50.871: I/child-thread(1356): child thread ID : 114
01-04 23:13:50.871: I/child-thread(1356): child thread name : Thread-114
Handlerを初期化するときにhandleMessage()メソッドを書き換えると、Handlerは変更され、具体的なエンジニアリングに関連します.
コードは次のとおりです.
public class ChildThread {
protected static final String TAG = "child-thread";
private Handler handler; // = new Handler();
public ChildThread(Handler handler) {
super();
this.handler = new Handler();
Log.i(TAG, "befor assign : " + this.handler.toString());
this.handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
}
};
Log.i(TAG, "AFTER assign : " + this.handler.toString());
}
public void doSth(){
new Thread(){
public void run() {
try {
//Looper.prepare();
sleep(2000);
handler.sendEmptyMessage(200);
Log.i(TAG, "msg send");
Log.i(TAG, "handler in child thread : " + handler.toString());
Log.i(TAG, "child thread ID : " + Thread.currentThread().getId());
Log.i(TAG, "child thread name : " + Thread.currentThread().getName());
//Looper.loop();
} catch (InterruptedException e) {
Log.e(TAG, "InterruptedException in child thread " + e.toString());
e.printStackTrace();
}
};
}.start();
}
}
ログの出力は次のとおりです.
01-04 23:21:44.381: I/child-thread(1405): befor assign : Handler (android.os.Handler) {4176bff0}
01-04 23:21:44.422: I/child-thread(1405): AFTER assign : Handler (com.example.msgdemo.ChildThread$1) {4176c740}
01-04 23:21:46.520: I/child-thread(1405): msg send
01-04 23:21:46.520: I/child-thread(1405): handler in child thread : Handler (com.example.msgdemo.ChildThread$1) {4176c740}
01-04 23:21:46.521: I/child-thread(1405): child thread ID : 117
01-04 23:21:46.521: I/child-thread(1405): child thread name : Thread-117
一方,呼び出し元から渡されたHandlerを用いると,呼び出し元のメッセージキューにメッセージを送信することができ,呼び出し元のLooperはメッセージキューからメッセージを取り出して処理することもできる.この方法は、単純なイン/スレッド間の同期に便利です.
サブスレッドコード:
public class ChildThread {
protected static final String TAG = "child-thread";
private Handler handler; // = new Handler();
public ChildThread(Handler handler) {
super();
this.handler = new Handler();
Log.i(TAG, "befor assign : " + this.handler.toString());
this.handler = handler;
// new handler ;
// handler handler
Log.i(TAG, "AFTER assign : " + this.handler.toString());
}
public void doSth(){
new Thread(){
public void run() {
try {
//Looper.prepare();
sleep(2000);
handler.sendEmptyMessage(200);
Log.i(TAG, "msg send");
Log.i(TAG, "handler in child thread : " + handler.toString());
Log.i(TAG, "child thread ID : " + Thread.currentThread().getId());
Log.i(TAG, "child thread name : " + Thread.currentThread().getName());
//Looper.loop();
} catch (InterruptedException e) {
Log.e(TAG, "InterruptedException in child thread " + e.toString());
e.printStackTrace();
}
};
}.start();
}
}
サブスレッドログ:
01-04 23:25:02.481: I/child-thread(1471): befor assign : Handler (android.os.Handler) {4176db78}
01-04 23:25:02.491: I/child-thread(1471): AFTER assign : Handler (com.example.msgdemo.MainActivity$1) {4174fe30}
01-04 23:25:04.524: I/child-thread(1471): msg send
01-04 23:25:04.524: I/child-thread(1471): handler in child thread : Handler (com.example.msgdemo.MainActivity$1) {4174fe30}
01-04 23:25:04.532: I/child-thread(1471): child thread ID : 123
01-04 23:25:04.532: I/child-thread(1471): child thread name : Thread-123
メインスレッドコード:
public class MainActivity extends Activity {
protected static final String TAG = "main-thread";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ChildThread ct = new ChildThread(mHandler);
ct.doSth();
}
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 200:
Log.i(TAG, "get 200 from child thread");
Log.i(TAG, "mHandler in main thread : " + mHandler.toString());
Log.i(TAG, "main thread ID : " + Thread.currentThread().getId());
Log.i(TAG, "main thread name : " + Thread.currentThread().getName());
break;
default:
break;
}
};
};
}
プライマリ・スレッド・ログ
01-04 23:25:04.524: I/main-thread(1471): get 200 from child thread
01-04 23:25:04.524: I/main-thread(1471): mHandler in main thread : Handler (com.example.msgdemo.MainActivity$1) {4174fe30}
01-04 23:25:04.524: I/main-thread(1471): main thread ID : 1
01-04 23:25:04.524: I/main-thread(1471): main thread name : main
比較ログでは、同じスレッドでは実行されないが、サブスレッドとプライマリスレッドのmHandlerは同じHandlerオブジェクトを指し、サブスレッドがこのHandlerオブジェクトを使用してプライマリスレッドにメッセージを正常に送信し、プライマリスレッドがこのメッセージを受信して処理していることがわかります.