现在搜索天数并且无法弄清楚为什么当设备的方向发生变化时,mediaPlayer会随机触发onCompletion。看起来那些具有更多内存的设备(如更强大的平板电脑)不会经常崩溃,但有时它们也会崩溃。
我的MediaPlayer正在Fragment中运行,我尝试使用持久的MediaPlayer技术调用setRetainInstance(true)。
作为提示,有些设备有时会在LogCat中说当主线程中发生了太多工作。
这是我的片段代码:
final public class MediaPlayerFragment extends Fragment implements OnCompletionListener, OnPreparedListener, OnBufferingUpdateListener, OnInfoListener, OnErrorListener, SurfaceHolder.Callback {
/**
*
*/
public final static String TAG = "MediaPlayerFragment";
private MediaPlayer mp = null;
private SurfaceView surfaceView = null;
private String path;
// create an handler
private final Handler myHandler = new Handler();
private static final double ASPECT_RATIO = 4.0 / 3.0;
/**
* Update the ui, so that background tasks can call this update on ui thread
*/
final Runnable updateUIRunnable = new Runnable() {
@Override
public void run() {
MediaPlayerFragment.this.updateSurfaceSizeToLayout();
}
};
@Override
public void onCreate(final Bundle savedInstanceState) {
Log.v(MediaPlayerFragment.TAG, "INFO: onCreate");
super.onCreate(savedInstanceState);
// Control whether a fragment instance is retained across Activity
// re-creation
this.setRetainInstance(true);
// get the extra data from the Intent (Container)
this.path = this.getArguments().getString("path");
if (this.path == null)
throw new RuntimeException("No path where passed to the mediaplayerFragment");
}
/**
* onCreateView
*
* Setup the view of the media player with a surface to show the mediaPlayer
*/
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
Log.v(MediaPlayerFragment.TAG, "INFO: onCreateView");
this.surfaceView = new SurfaceView(this.getActivity());
// Push surfaceView to maximum available space
final android.widget.FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
this.surfaceView.setLayoutParams(layoutParams);
// Placeholder for the area of the video, region to display
final SurfaceHolder surfaceHolder = this.surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setSizeFromLayout();
return this.surfaceView;
}
@Override
public void onPause() {
Log.v(MediaPlayerFragment.TAG, "onPause()");
if (this.mp != null) {
this.mp.pause();
this.mp.setDisplay(null);
}
super.onPause();
}
/**
* Prepare the media player for playback. This sets the filepath and start
* the buffering for the player
*/
private void preparePlayer() {
Log.v(MediaPlayerFragment.TAG, "preparePlayer");
try {
this.mp.setLooping(false);
this.mp.setDataSource(this.path);
this.mp.prepareAsync();
}
catch (final IOException e) {
Log.e(MediaPlayerFragment.TAG, "ERROR: Caught ioExeption" + e.getMessage(), e);
}
catch (final Exception e) {
// IllegalStateException if it is called in an invalid state
Log.e(MediaPlayerFragment.TAG, "ERROR: " + e.getMessage(), e);
}
}
@Override
public void onCompletion(final MediaPlayer arg0) {
Log.d(MediaPlayerFragment.TAG, "onCompletion called");
this.releaseMediaPlayer();
}
@Override
public void onPrepared(final MediaPlayer mediaplayer) {
Log.d(MediaPlayerFragment.TAG, "onPrepared called");
if (!this.mp.isPlaying()) {
this.mp.start();
}
}
@Override
public boolean onError(final MediaPlayer arg0, final int arg1, final int arg2) {
Log.v(MediaPlayerFragment.TAG, "onError MediaPlayer");
return false;
}
/**
* Release the media player object and set its variable to null.
*/
private void releaseMediaPlayer() {
if (this.mp != null) {
this.mp.reset();
this.mp.release();
this.mp = null;
}
}
/**
* display is turned
*/
@Override
public void surfaceCreated(final SurfaceHolder holder) {
if (this.mp == null) {
// get a MediaPlayer to display the video
// store the MediaPlayer in the Application, so it does not get
this.mp = new MediaPlayer();
this.mp.setOnCompletionListener(this);
this.mp.setOnPreparedListener(this);
this.mp.setOnBufferingUpdateListener(this);
this.mp.setOnInfoListener(this);
this.mp.setOnErrorListener(this);
this.mp.setScreenOnWhilePlaying(true);
this.mp.setDisplay(holder);
this.preparePlayer();
} else {
// re-establish connection to given surfaceHolder
this.mp.setDisplay(holder);
}
}
@Override
public void surfaceDestroyed(final SurfaceHolder surfaceHolder1) {
Log.v(MediaPlayerFragment.TAG, "surfaceDestroyed called");
// disengage old surface holder
if (this.mp != null) {
this.mp.setDisplay(null);
}
}
/**
* This is called immediately after any structural changes (format or size)
* have been made to the surface. You should at this point update the
* imagery in the surface. This method is always called at least once, after
* surfaceCreated(SurfaceHolder).
*
* @param holder
* The SurfaceHolder whose surface has changed.
* @param format
* The new PixelFormat of the surface.
* @param width
* The new width of the surface.
* @param height
* The new height of the surface.
*/
@Override
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
Log.v(MediaPlayerFragment.TAG, "surfaceChanged called");
// disengage old surface holder
if (this.mp != null) {
this.myHandler.post(this.updateUIRunnable);
this.mp.setDisplay(holder);
}
}
/**
* The looks at the dimensions of the surfaceView that holds the MediaPlayer
* (Video) and update the size of it to fit that dimension. This is
* necessary because of orientation changes that alters the possible height
* and width of the surface on that the video is drawn.
*/
@SuppressWarnings("boxing")
public void updateSurfaceSizeToLayout() {
// Surface.ROTATION_0 (no rotation), Surface.ROTATION_90,
// Surface.ROTATION_180, or Surface.ROTATION_270
final int orientation = this.getResources().getConfiguration().orientation;
Log.d(MediaPlayerFragment.TAG, "Orientation :" + orientation);
// Get the SurfaceView layout parameters
final android.view.ViewGroup.LayoutParams lp = this.surfaceView.getLayoutParams();
final FrameLayout frameLayout = (FrameLayout) this.surfaceView.getParent();
final int width = frameLayout.getWidth();
final int height = frameLayout.getHeight();
if (orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
// Display in portrait
lp.width = LayoutParams.MATCH_PARENT;
lp.height = (int) (width / MediaPlayerFragment.ASPECT_RATIO);
Log.d(MediaPlayerFragment.TAG, String.format("New surfaceView size: width:max ,height:%d", lp.height));
} else {
lp.width = (int) (height * MediaPlayerFragment.ASPECT_RATIO);
lp.height = LayoutParams.MATCH_PARENT;
Log.d(MediaPlayerFragment.TAG, String.format("New surfaceView size: width:%d , height: max", lp.width));
}
// Commit the layout parameters
this.surfaceView.setLayoutParams(lp);
this.surfaceView.getHolder().setSizeFromLayout();
}
}
任何人都可以看到我如何处理MediaPlayer的SurfaceView或其他东西的错误,以免应用程序崩溃。
以下是LogCat旋转两次后的日志,并在第二个方向更改后崩溃:
09-02 18:59:05.796: W/MediaPlayer(19521): info/warning (702, 0)
09-02 18:59:05.806: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:702, extra:0
09-02 18:59:05.836: W/MediaPlayer(19521): info/warning (701, 0)
09-02 18:59:05.836: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:701, extra:0
09-02 18:59:06.356: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:06.436: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:06.466: V/MediaPlayerFragment(19521): onDetach
09-02 18:59:06.546: D/MPActivity(19521): onCreate
09-02 18:59:06.546: V/MediaPlayerFragment(19521): DEBUG: onAttach
09-02 18:59:06.596: D/MPActivity(19521): addVideoFragment
09-02 18:59:06.596: V/MediaPlayerFragment(19521): INFO: onCreateView
09-02 18:59:06.606: V/MediaPlayerFragment(19521): DEBUG: onResume
09-02 18:59:06.696: D/MediaPlayerFragment(19521): surfaceCreated called
09-02 18:59:06.726: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:06.796: D/MediaPlayerFragment(19521): Orientation :2
09-02 18:59:06.796: D/MediaPlayerFragment(19521): width:810 ,height:-1
09-02 18:59:06.826: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:06.906: D/MediaPlayerFragment(19521): Orientation :2
09-02 18:59:06.916: D/MediaPlayerFragment(19521): width:810 ,height:-1
09-02 18:59:07.886: W/MediaPlayer(19521): info/warning (702, 0)
09-02 18:59:07.886: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:702, extra:0
09-02 18:59:08.186: W/MediaPlayer(19521): info/warning (701, 0)
09-02 18:59:08.186: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:701, extra:0
09-02 18:59:09.036: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:09.056: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:09.066: V/MediaPlayerFragment(19521): onDetach
09-02 18:59:09.086: E/MediaPlayer(19521): error (1, -2147483648)
09-02 18:59:09.086: D/MPActivity(19521): onCreate
09-02 18:59:09.086: V/MediaPlayerFragment(19521): DEBUG: onAttach
09-02 18:59:09.116: D/MPActivity(19521): addVideoFragment
09-02 18:59:09.116: V/MediaPlayerFragment(19521): INFO: onCreateView
09-02 18:59:09.126: V/MediaPlayerFragment(19521): DEBUG: onResume
09-02 18:59:09.176: D/MediaPlayerFragment(19521): surfaceCreated called
09-02 18:59:09.196: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:09.196: E/MediaPlayer(19521): Error (1,-2147483648)
09-02 18:59:09.196: V/MediaPlayerFragment(19521): onError MediaPlayer
09-02 18:59:09.196: D/MediaPlayerFragment(19521): onCompletion called
09-02 18:59:09.366: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:09.376: D/MediaPlayerFragment(19521): Orientation :1
09-02 18:59:09.376: D/MediaPlayerFragment(19521): width:-1 ,height:600
09-02 18:59:09.376: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:09.396: D/VideoListActivity(19521): onResume()
09-02 18:59:09.496: V/MediaPlayerFragment(19521): INFO: onDestroy
09-02 18:59:09.496: V/MediaPlayerFragment(19521): onDetach
答案 0 :(得分:0)
关于OnCompletionListener
这是某些三星设备中发生的典型错误。克服它的唯一方法是,保持标准播放器是检查视频资产的长度,如果当前位置不接近结束,则应忽略OnCompletion事件。
关于setRetainInstance(true)
:
调用此方法时,片段生命周期将为:
onCreate(只需一次,无论旋转多少次)
onCreateView(每次旋转设备时) - 此处重新创建所有视图,因此此时您的表面,持有者无效。
等...
看看我创建的允许轮换的POC。 How to play audio continuously while orientation changes in Android?
祝你好运