この問題は私を夢中にさせます。アクティビティ内で作成された新しいスレッドで長い操作を処理する方法、および長い操作の実行後にテキストなどのビューコンポーネントを変更する方法について、基本的ですが非常に重要な知識が不足しています。
最初に、この問題が発生するコードの部分を示します。
mProgressDialog = ProgressDialog.show(mContext, "Tripplanner", "please wait...", true, false);
connectAndGetRoute();
private void connectAndGetRoute(){
new Thread(){
@Override
public void run() {
try {
if(!connectTo9292ov()) return;// conncetto9292ov() connects to a website, parses the reasult into an arraylist. The arraylist contains route.
} catch(UnknownHostException e){
Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
}catch (ClientProtocolException e) {
Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
}
handler.post(runConnection);
}
}.start();
handler = new Handler();
runConnection = new Runnable(){
@Override
public void run() {
mProgressDialog.dismiss();
showOnScreen();
}
};
}
そしてこれは私が得るエラーです:
ERROR/WindowManager(8297): Activity mp.tripplanner.OvPlanner has leaked window com.Android.internal.policy.impl.PhoneWindow$DecorView@46368a28 that was originally added here
ERROR/WindowManager(8297): Android.view.WindowLeaked: Activity mp.tripplanner.OvPlanner has leaked window com.Android.internal.policy.impl.PhoneWindow$DecorView@46368a28 that was originally added here
ERROR/WindowManager(8297): at Android.view.ViewRoot.<init>(ViewRoot.Java:251)
ERROR/WindowManager(8297): at Android.view.WindowManagerImpl.addView(WindowManagerImpl.Java:148)
ERROR/WindowManager(8297): at Android.view.WindowManagerImpl.addView(WindowManagerImpl.Java:91)
ERROR/WindowManager(8297): at Android.view.Window$LocalWindowManager.addView(Window.Java:424)
ERROR/WindowManager(8297): at Android.app.Dialog.show(Dialog.Java:241)
ERROR/WindowManager(8297): at Android.app.ProgressDialog.show(ProgressDialog.Java:107)
ERROR/WindowManager(8297): at Android.app.ProgressDialog.show(ProgressDialog.Java:95)
ERROR/WindowManager(8297): at mp.tripplanner.OvPlanner$3.onClick(OvPlanner.Java:351)
ERROR/WindowManager(8297): at Android.view.View.performClick(View.Java:2408)
ERROR/WindowManager(8297): at Android.view.View$PerformClick.run(View.Java:8817)
ERROR/WindowManager(8297): at Android.os.Handler.handleCallback(Handler.Java:587)
ERROR/WindowManager(8297): at Android.os.Handler.dispatchMessage(Handler.Java:92)
ERROR/WindowManager(8297): at Android.os.Looper.loop(Looper.Java:144)
ERROR/WindowManager(8297): at Android.app.ActivityThread.main(ActivityThread.Java:4937)
ERROR/WindowManager(8297): at Java.lang.reflect.Method.invokeNative(Native Method)
ERROR/WindowManager(8297): at Java.lang.reflect.Method.invoke(Method.Java:521)
ERROR/WindowManager(8297): at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:868)
ERROR/WindowManager(8297): at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:626)
ERROR/WindowManager(8297): at dalvik.system.NativeStart.main(Native Method)
ただし、上記のエラーメッセージの前に別のエラーメッセージがログに書き込まれます。これは次のとおりです。
ERROR/AndroidRuntime(8297): FATAL EXCEPTION: Thread-9
ERROR/AndroidRuntime(8297): Java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(8297): at Android.os.Handler.<init>(Handler.Java:121)
ERROR/AndroidRuntime(8297): at Android.widget.Toast.<init>(Toast.Java:68)
ERROR/AndroidRuntime(8297): at Android.widget.Toast.makeText(Toast.Java:231)
ERROR/AndroidRuntime(8297): at mp.tripplanner.OvPlanner$4.run(OvPlanner.Java:371)
ご協力ありがとうございました。
UIを更新できるようにするには、UIスレッドでHandler
を作成する必要があります。次に、ランナブルを投稿するのではなく、ハンドラーのsendMessage
メソッドを使用します。
private static final int HANDLER_MESSAGE_ERROR = 0;
private static final int HANDLER_MESSAGE_COMPLETED = 1;
...
private void connectAndGetRoute(){
new Thread(){
@Override
public void run() {
try {
if(!connectTo9292ov()) return;
} catch(UnknownHostException e){
sendMessage(HANDLER_MESSAGE_ERROR);
} catch (ClientProtocolException e) {
sendMessage(HANDLER_MESSAGE_ERROR);
} catch (IOException e) {
sendMessage(HANDLER_MESSAGE_ERROR);
} finally {
sendMessage(HANDLER_MESSAGE_COMPLETED);
}
}
private void sendMessage(int what){
Message msg = Message.obtain();
msg.what = what;
mHandler.sendMessage(msg);
}
}.start();
}
...
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case HANDLER_MESSAGE_ERROR:
Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
break;
case HANDLER_MESSAGE_COMPLETED:
mProgressDialog.dismiss();
showOnScreen();
break;
default:
Log.w("MyTag","Warning: message type \""+msg.what+"\" not supported");
}
}
}
簡単なアイデアです。
スレッド内のトーストメッセージだと思います。それらをコメントアウトしてみてください。
それでもメッセージを表示したい場合は、スレッドのステータスを保存して、ハンドラーで処理してください。最終的にブロックすることからハンドラーを呼び出す
mProgressDialog = ProgressDialog.show(mContext, "Tripplanner", "please wait...", true, false);
connectAndGetRoute();
private void connectAndGetRoute(){
new Thread(){
@Override
public void run() {
try {
if(!connectTo9292ov()) return;// conncetto9292ov() connects to a website, parses the reasult into an arraylist. The arraylist contains route.
} catch(UnknownHostException e){
// an int member of your activity
threadStatus = 404;
// Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
}catch (ClientProtocolException e) {
threadStatus = 404;
// Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
} catch (IOException e) {
threadStatus = 404;
// Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();
}
finally {
handler.post(runConnection);}
}
}.start();
handler = new Handler();
runConnection = new Runnable(){
@Override
public void run() {
if (threadStatus == 404) { Toast.makeText(mContext, "failed to connect to server", Toast.LENGTH_LONG).show();}
else {
mProgressDialog.dismiss();
showOnScreen();}
}
};
}
サンプルコード:
new Thread(new Runnable() {
@Override
public void run() {
doNotUIthings();
updateUI.sendEmptyMessage(0); //Update ui in UI thread
}
private Handler updateUI = new Handler(){
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
updateUI();
}
};
}).start();
ただし、Javaスレッドの代わりに AsyncTask を使用することをお勧めします。