Android MediaPlayer:应用 closes/press 后退按钮时出现 IllegalStateException

Android MediaPlayer : IllegalStateException when app closes/press back button

我正在使用媒体播放器从 url 流式传输 mp3 音频,现在我可以播放音乐,但是当 我按下后退按钮或关闭应用程序时,它会崩溃。 谁能帮我找出我的错误。 谢谢。

我的代码是:

        private ImageView play, forward, backward;
        private MediaPlayer mediaPlayer;
        private boolean playing = false;
        private ProgressDialog dialog;
        private String mp3link;
        private SeekBar seekbar;
        private Handler handler = new Handler();
        private int mediaPos;
        private int mediaMax;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            final String url ="";
            initWidgets();

        }

    private void initWidgets() {
        mp3link = "http://loc8app.com/church/uploads/audio/749928ad6fcb7b1aceefdf03bd7a9465.mp3";
        play = (ImageView) findViewById(R.id.control);
        seekbar = (SeekBar) findViewById(R.id.seekBar);
//        forward = (ImageView) findViewById(R.id.playeer_forward);
//        backward = (ImageView) findViewById(R.id.playeer_back);
        mediaPlayer = new MediaPlayer();
        play.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                playFunction();
            }
        });

        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if(mediaPlayer != null && fromUser){
                    mediaPlayer.seekTo(progress);
                }
            }
        });
    }


    private void playFunction() {
        if (!playing) {
            try {
                dialog = ProgressDialog
                        .show(MainActivity.this,
                                "",
                                getString(com.root5solutions.music.R.string.buffering),
                                true);
                dialog.setCancelable(true);
                dialog.show();
                mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mediaPlayer.setDataSource(mp3link);
                mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        play.setBackgroundResource(R.drawable.pause);
                        playing = true;
                        //this is new
                        mediaPos = mp.getCurrentPosition();
                        mediaMax = mp.getDuration();

                        seekbar.setMax(mediaMax);
                        seekbar.setProgress(mediaPos);
                        //this line is the error
                        handler.removeCallbacks(moveSeekBarThread);
                        handler.postDelayed(moveSeekBarThread, 100);

                        mp.start();
                        dialog.dismiss();
                    }
                });
                mediaPlayer.prepareAsync();

            } catch (Exception e) {
                e.printStackTrace();
                dialog.dismiss();
            }
        } else {
            play.setBackgroundResource(R.drawable.play);
            mediaPlayer.stop();
            mediaPlayer.release();
            playing = false;
        }
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }

    private Runnable moveSeekBarThread = new Runnable() {

        public void run() {
            if (mediaPlayer.isPlaying()) {
                int mediaPos_new = mediaPlayer.getCurrentPosition();
                int mediaMax_new = mediaPlayer.getDuration();
                seekbar.setMax(mediaMax_new);
                seekbar.setProgress(mediaPos_new);

                handler.postDelayed(this, 1000); // Looping the thread after 1 second

            }

        }
    };
}

Logcat 显示:

E/AndroidRuntime: FATAL EXCEPTION: main
                                                                          Process: com.root.music, PID: 26981
                                                                          java.lang.IllegalStateException
                                                                              at android.media.MediaPlayer.isPlaying(Native Method)
                                                                              at com.root.music.MainActivity.run(MainActivity.java:132)
                                                                              at android.os.Handler.handleCallback(Handler.java:739)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                              at android.os.Looper.loop(Looper.java:135)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5351)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at java.lang.reflect.Method.invoke(Method.java:372)
                                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:947)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:742)

你得到 IllegalStateException .

Signals that a method has been invoked at an illegal or inappropriate time .

if 条件

之后调用 super.onBackPressed();
 @Override 
public void onBackPressed() 
 {
      if (mediaPlayer!= null)
     {
             if(mediaPlayer.isPlaying())
              mediaPlayer.stop();
              mediaPlayer.release();
      }

     super.onBackPressed(); // Call here
 }

问题似乎是由 moveSeekBarThread Runnable 引起的,从中引发异常,在 onBackPressed() 释放 mediaPlayer 后继续执行.这导致 isPlaying() 方法被执行,根据 documentation 将导致 IllegalStateException:

if the internal player engine has not been initialized or has been released.

查看 moveSeekBarThread,它似乎被配置为通过延迟将自己发送回 handler Handler 实例来无休止地重新安排自己。当用户离开 activity 时,此过程不会停止,这解释了为什么 moveSeekBarThread 保留 运行。因此,基于以上所述,一种解决方案可能是确保在用户离开 activity 时调用 mediaPlayer.release() 之前删除 handler 队列中的任何 moveSeekBarThread 实例].

您应该可以通过在调用 mediaPlayer.release() 之前调用 handler.removeCallbacks(moveSeekBarThread); 来实现。例如如下:

@Override
public void onBackPressed() {
    super.onBackPressed();
    handler.removeCallbacks(moveSeekBarThread);
    if (mediaPlayer.isPlaying()) {
        mediaPlayer.stop();
        mediaPlayer.release();
    }
}

mediaPlayer.release()之前调用应该没问题,但我觉得不管mediaPlayer是否在播放都调用它比较安全。这样,如果 Runnable 确实以某种方式启动或保持启动状态,尽管媒体播放器尚未启动或已停止,Runnable 仍将被清除。

顺便说一句,虽然我没有任何使用 MediaPlayer 的经验,但我偶然注意到 release 方法的文档有以下内容:

It is considered good practice to call this method when you're done using the MediaPlayer. In particular, whenever an Activity of an application is paused (its onPause() method is called), or stopped (its onStop() method is called), this method should be invoked to release the MediaPlayer object, unless the application has a special need to keep the object around. In addition to unnecessary resources (such as memory and instances of codecs) being held, failure to call this method immediately if a MediaPlayer object is no longer needed may also lead to continuous battery consumption for mobile devices, and playback failure for other applications if no multiple instances of the same codec are supported on a device.

因此,除非在您的情况下有特殊需要将媒体播放器保留在 activity 中,否则最好处理发布过程(包括从 [=18 中清除 moveSeekBarThread =]) 在 onPauseonStop 中。

希望对您有所帮助!

首先你需要明白illegalStateException是什么意思:

根据 Android 文档:

It Signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation.

看看媒体播放器的状态图:

https://developer.android.com/images/mediaplayer_state_diagram.gif

调用setDataSource(FileDescriptor),或setDataSource(String),或setDataSource(Context, Uri),或setDataSource(FileDescriptor, long, long),或setDataSource(MediaDataSource)将Idle状态的MediaPlayer对象传输到初始化状态。 如果在任何其他状态下调用 setDataSource(),则会抛出 IllegalStateException。

始终注意可能从重载的 setDataSource 方法中抛出的 IllegalArgumentException 和 IOException 是一种很好的编程习惯。