Activity has leaked window com.android.internal.po

2019-01-14 23:32发布

问题:

This problem drives me crazy. I miss some basic but very important knowledge about how to handle long operations in a new thread created within an activity and how to modify view components like text and such after the long operation's done.

Let me first show you the part of my code where this problem happens:

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();
        }   
    };
}

and this is the error I get:

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)

But another error message is written to the log before the above one, which is:

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)

Thank you for your help.

回答1:

Your Handler needs to be created in your UI thread for it to be able to update the UI. I would then use the sendMessage method of the handler, rather than posting a runnable:

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");
        }
    }
}


回答2:

Sample code:

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();

But I recommend to use AsyncTask instead of Java Threads.



回答3:

Just a quick idea.

i think, it's your toast-messages within thread. Try to comment them out.

If you still want to show messages, save the status of your thread and handle it in your handler. Call for that your handler from finally block

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();}
        }   
    };
}