androidmemory-leaksprogressdialogtoast

WindowLeaked from Dialog


I'm having a typical WindowLeaked exception

03-03 21:03:26.441: ERROR/WindowManager(631): Activity com.myapp.Player has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@45136c38 that was originally added here 03-03 21:03:26.441: ERROR/WindowManager(631): android.view.WindowLeaked: Activity com.myapp.Player has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@45136c38 that was originally added here 03-03 21:03:26.441: ERROR/WindowManager(631): at android.view.ViewRoot.(ViewRoot.java:247) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.view.Window$LocalWindowManager.addView(Window.java:424) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.app.Dialog.show(Dialog.java:241) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.app.AlertDialog$Builder.show(AlertDialog.java:802) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.widget.VideoView$4.onError(VideoView.java:387) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:1264) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.os.Handler.dispatchMessage(Handler.java:99) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.os.Looper.loop(Looper.java:123) 03-03 21:03:26.441: ERROR/WindowManager(631): at android.app.ActivityThread.main(ActivityThread.java:4627) 03-03 21:03:26.441: ERROR/WindowManager(631): at java.lang.reflect.Method.invokeNative(Native Method) 03-03 21:03:26.441: ERROR/WindowManager(631): at java.lang.reflect.Method.invoke(Method.java:521) 03-03 21:03:26.441: ERROR/WindowManager(631): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 03-03 21:03:26.441: ERROR/WindowManager(631): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 03-03 21:03:26.441: ERROR/WindowManager(631): at dalvik.system.NativeStart.main(Native Method)

None of the answers I've read so far seem to solve it. Here is the code:

mVideoView.setOnErrorListener(new OnErrorListener() {
    @Override
    public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
        Toast.makeText(Player.this, "Sorry, unable to play this video", Toast.LENGTH_LONG).show();
        progressDialog.dismiss();
        if (mToken != null) {
            MusicUtils.unbindFromService(mToken);
        }
        finish();
        return false;
    }
});

The only guess I was left with was that the toast was holding on to the activity, but everything I'm reading seems to say the toast doesn't matter. I even tried using getBaseContext(), and then tried putting the toast after finish() to see if it would work. I'm all out of ideas so any help would be awesome.

UPDATE: Here is more of the code

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        extras = getIntent().getExtras();
        media_url = extras.getString("media_url");
        setContentView(R.layout.video_player);
        //Start progress dialog so user knows something is going on
        progressDialog = ProgressDialog.show(this, "", "Loading...", true);
        mVideoView = (VideoView) findViewById(R.id.surface_view);

        runOnUiThread(new Runnable(){
            public void run() {
                playVideo();
            }
        });
    }

    private void playVideo() {
        try {
            if (media_url == null || media_url.length() == 0) {
                progressDialog.dismiss();
                Toast.makeText(VideoPlayer.this, "File URL/media_url is empty",
                        Toast.LENGTH_LONG).show();

            } else {
                // If the path has not changed, just start the media player
                if (media_url.equals(current) && mVideoView != null) {
                    mVideoView.start();
                    mVideoView.requestFocus();
                    return;
                }
                current = media_url;
                mVideoView.setVideoURI(Uri.parse(media_url));
                ctlr=new MediaController(VideoPlayer.this);
                ctlr.setMediaPlayer(mVideoView);
                mVideoView.setMediaController(ctlr);
                mVideoView.requestFocus();
                mVideoView.setOnPreparedListener(new OnPreparedListener() {

                    public void onPrepared(MediaPlayer arg0) {
                        progressDialog.dismiss();
                        mVideoView.start();
                    }
                });
                mVideoView.setOnErrorListener(new OnErrorListener() {

                    @Override
                    public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
                        Toast.makeText(VideoPlayer.this, "Sorry, unable to play this video",
                                Toast.LENGTH_LONG).show();
                        progressDialog.dismiss();
                        if (mToken != null) {
                            MusicUtils.unbindFromService(mToken);
                        }
                        finish();
                        return false;
                    }
                });


            }
        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage(), e);
            if (mVideoView != null) {
                mVideoView.stopPlayback();
            }
            finish();
        }
    }

    private void startPlayback() {

        if(mService == null)
            return;
        Intent intent = getIntent();
        String filename = "";
        Uri uri = intent.getData();
        if (uri != null && uri.toString().length() > 0) {
            String scheme = uri.getScheme();
            if ("file".equals(scheme)) {
                filename = uri.getPath();
            } else {
                filename = uri.toString();
            }
            try {
                mService.stop();
                mService.openFileAsync(filename);
                mService.play();
                setIntent(new Intent());
            } catch (Exception ex) {
                Log.e(tag, "couldn't start playback: " + ex);
            }
        }
    }

    private ServiceConnection osc = new ServiceConnection() {
        public void onServiceConnected(ComponentName classname, IBinder obj) {
            mService = IMediaPlaybackService.Stub.asInterface(obj);
            startPlayback();
        }
        public void onServiceDisconnected(ComponentName classname) {
            mService = null;
        }
     };

Error is occurring when it is a video type the device can't play


Solution

  • Maybe you could try to show your Toast through runOnUiThread ?

    Something like:

        @Override
        public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
           Player.this.runOnUiThread(new Runnable() {
               void run() {
                   Toast.makeText(Player.this, "Sorry, unable to play this video", Toast.LENGTH_LONG).show();
                   progressDialog.dismiss();
                   if (mToken != null) {
                        MusicUtils.unbindFromService(mToken);
                    }
                    finish();
               }
            );
            return false;
        }
    

    EDIT:

    Here is more...below is the code (in Froyo) for the VideoView.

    Seems like the error comes from the AlertDialog.Builder... but based on your code, it should not even get there since the error should be handled higher when mOnErrorListener is not null...

    Can you check whether your error handler is being called ? Maybe try to not call finish() there ?

     private MediaPlayer.OnErrorListener mErrorListener =
            new MediaPlayer.OnErrorListener() {
            public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
                Log.d(TAG, "Error: " + framework_err + "," + impl_err);
                mCurrentState = STATE_ERROR;
                mTargetState = STATE_ERROR;
                if (mMediaController != null) {
                    mMediaController.hide();
                }
    
                /* If an error handler has been supplied, use it and finish. */
                if (mOnErrorListener != null) {
                    if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
                        return true;
                    }
                }
    
                /* Otherwise, pop up an error dialog so the user knows that
                 * something bad has happened. Only try and pop up the dialog
                 * if we're attached to a window. When we're going away and no
                 * longer have a window, don't bother showing the user an error.
                 */
                if (getWindowToken() != null) {
                    Resources r = mContext.getResources();
                    int messageId;
    
                    if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
                        messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
                    } else {
                        messageId = com.android.internal.R.string.VideoView_error_text_unknown;
                    }
    
                    new AlertDialog.Builder(mContext)
                            .setTitle(com.android.internal.R.string.VideoView_error_title)
                            .setMessage(messageId)
                            .setPositiveButton(com.android.internal.R.string.VideoView_error_button,
                                    new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {
                                            /* If we get here, there is no onError listener, so
                                             * at least inform them that the video is over.
                                             */
                                            if (mOnCompletionListener != null) {
                                                mOnCompletionListener.onCompletion(mMediaPlayer);
                                            }
                                        }
                                    })
                            .setCancelable(false)
                            .show();
                }
                return true;
            }
        };
    

    MORE EDIT:

    Change the onError handler to return true;

    Then the "default" error handler will not try to build this AlertDialog