Android MediaPlayer AudioStream AudioFlinger сервер умер!, Фатальный сигнал 11
У меня есть два фрагмента (левый и правый) и получение в левом фрагменте списка Радиостримов. Нажав на один из этих потоков, правый фрагмент должен изменить имя потока и начать воспроизведение потока с заданным URI.
2 проблемы:
- Некоторые из радиопотоков не обновлены, поэтому многие из них больше не работают. Проблема в том, что это заставляет мое приложение делать принудительное закрытие! Я сделал обработку ошибок, но после вызова такого потока я получаю:
03-20 14: 23: 28.192: A / libc (1021): фатальный сигнал 11 (SIGSEGV) при 0x00000000 (код = 1)
03-20 14: 23: 28.192: W / AudioSystem (1021): Сервер AudioFlinger умер!
03-20 14: 23: 28.192: W / IMediaDeathNotifier (1021): коммуникационный сервер умер
03-20 14:23:28.192: E/MediaPlayer(1021): ошибка (100, 0)
03-20 14: 23: 28.192: I / ServiceManager (1021): Ожидание службы media.audio_flinger...
03-20 14: 23: 28.752: I / dalvikvm (1021): threadid = 3: реагирование на сигнал 3
03-20 14: 23: 28.782: I / dalvikvm (1021): записал трассировки стека в /data/anr/traces.txt
03-20 14:23:29.192: I/ServiceManager(1021): Ожидание службы media.audio_flinger...
Я не знаю почему. Есть ли другой способ обработки ошибок? Или есть способ проверить все потоки перед вызовом mediaPlayer.setDataSource(uri), чтобы избежать подготовки дефектного uris? (см. мой код в конце)
- Я контролирую левый ListFragment с помощью пульта дистанционного управления. Когда я пытаюсь очень быстро переключаться с одного канала на другой, все происходит очень медленно. Кажется, что переустановка медиаплеера займет очень много времени. Когда я не восстанавливаю, я получаю ошибку времени выполнения, когда я снова вызываю mediaPlayer.setDataSource(..). Есть ли способ вызвать.setDataSource два раза для одного объекта MediaPlayer?
Вот мой код: Мой класс MediaPlayer Wrapper:
package net.smart4life.tvplay.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.Log;
/**
* A wrapper class for {@link android.media.MediaPlayer}.
* <p>
* Encapsulates an instance of MediaPlayer, and makes a record of its internal
* state accessible via a {@link MediaPlayerWrapper#getState()} accessor.
*/
public class MediaPlayerStateWrapper {
private static String tag = "MediaPlayerWrapper";
private MediaPlayer mPlayer;
private State currentState;
private MediaPlayerStateWrapper mWrapper;
public MediaPlayerStateWrapper() {
mWrapper = this;
mPlayer = new MediaPlayer();
currentState = State.IDLE;
mPlayer.setOnPreparedListener(mOnPreparedListener);
mPlayer.setOnCompletionListener(mOnCompletionListener);
mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
mPlayer.setOnErrorListener(mOnErrorListener);
mPlayer.setOnInfoListener(mOnInfoListener);
}
/* METHOD WRAPPING FOR STATE CHANGES */
public static enum State {
IDLE, ERROR, INITIALIZED, PREPARING, PREPARED, STARTED, STOPPED, PLAYBACK_COMPLETE, PAUSED;
}
public void setDataSource(String path) {
if (currentState == State.IDLE) {
try {
mPlayer.setDataSource(path);
currentState = State.INITIALIZED;
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else
throw new RuntimeException();
}
public void prepareAsync() {
Log.d(tag, "prepareAsync()");
if (EnumSet.of(State.INITIALIZED, State.STOPPED).contains(currentState)) {
mPlayer.prepareAsync();
currentState = State.PREPARING;
} else
throw new RuntimeException();
}
public boolean isPlaying() {
Log.d(tag, "isPlaying()");
if (currentState != State.ERROR) {
return mPlayer.isPlaying();
} else
throw new RuntimeException();
}
public void seekTo(int msec) {
Log.d(tag, "seekTo()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.seekTo(msec);
} else
throw new RuntimeException();
}
public void pause() {
Log.d(tag, "pause()");
if (EnumSet.of(State.STARTED, State.PAUSED).contains(currentState)) {
mPlayer.pause();
currentState = State.PAUSED;
} else
throw new RuntimeException();
}
public void start() {
Log.d(tag, "start()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.start();
currentState = State.STARTED;
} else
throw new RuntimeException();
}
public void stop() {
Log.d(tag, "stop()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.STOPPED,
State.PAUSED, State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.stop();
currentState = State.STOPPED;
} else
throw new RuntimeException();
}
public void reset() {
Log.d(tag, "reset()");
mPlayer.reset();
currentState = State.IDLE;
}
/**
* @return The current state of the mediaplayer state machine.
*/
public State getState() {
Log.d(tag, "getState()");
return currentState;
}
public void release() {
Log.d(tag, "release()");
mPlayer.release();
}
/* INTERNAL LISTENERS */
private OnPreparedListener mOnPreparedListener = new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
Log.d(tag, "on prepared");
currentState = State.PREPARED;
mWrapper.onPrepared(mp);
mPlayer.start();
currentState = State.STARTED;
}
};
private OnCompletionListener mOnCompletionListener = new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Log.d(tag, "on completion");
currentState = State.PLAYBACK_COMPLETE;
mWrapper.onCompletion(mp);
}
};
private OnBufferingUpdateListener mOnBufferingUpdateListener = new OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
Log.d(tag, "on buffering update");
mWrapper.onBufferingUpdate(mp, percent);
}
};
private OnErrorListener mOnErrorListener = new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on error");
currentState = State.ERROR;
mWrapper.onError(mp, what, extra);
return false;
}
};
private OnInfoListener mOnInfoListener = new OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on info");
mWrapper.onInfo(mp, what, extra);
return false;
}
};
/* EXTERNAL STUBS TO OVERRIDE */
public void onPrepared(MediaPlayer mp) {
}
public void onCompletion(MediaPlayer mp) {
}
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.stop();
mp.release();
return false;
}
public boolean onInfo(MediaPlayer mp, int what, int extra) {
return false;
}
/* OTHER STUFF */
public int getCurrentPosition() {
if (currentState != State.ERROR) {
return mPlayer.getCurrentPosition();
} else {
return 0;
}
}
public int getDuration() {
// Prepared, Started, Paused, Stopped, PlaybackCompleted
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.STOPPED, State.PLAYBACK_COMPLETE).contains(currentState)) {
return mPlayer.getDuration();
} else {
return 100;
}
}
}
Вот мой TestFragment (правый фрагмент). Примечание: левый фрагмент вызывает метод "newChannel(radioChannel)" из TestFragment, каждый раз при нажатии на элемент списка.
package net.smart4life.tvplay.fragment;
import java.io.IOException;
import net.smart4life.tvplay.R;
import net.smart4life.tvplay.model.MediaPlayerStateWrapper;
import net.smart4life.tvplay.model.RadioChannel;
import android.app.Fragment;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class TestFragment extends Fragment {
private RadioChannel radioCh;
private TextView tv_RadioCh;
private MediaPlayerStateWrapper mediaWrapper;
private View view;
// firstcall
public TestFragment(RadioChannel radioChannel) {
this.radioCh = radioChannel;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
tv_RadioCh = (TextView) view.findViewById(R.id.radioText);
mediaWrapper = new MediaPlayerStateWrapper();
newChannel(radioCh);
}
public void newChannel (RadioChannel radioChannel) {
this.radioCh = radioChannel;
Log.e("RadioChannel", radioCh.getName());
tv_RadioCh.setText(radioCh.getName());
if(mediaWrapper.isPlaying()) {
mediaWrapper.stop();
mediaWrapper.reset();
} else if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.PREPARING) {
mediaWrapper.release();
mediaWrapper = new MediaPlayerStateWrapper();
}
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_radio_player, container,
false);
return view;
}
@Override
public void onDetach() {
super.onDetach();
mediaWrapper.release();
}
}
Профи, не могли бы вы помочь мне с одним или обоими вопросами?
2 ответа
Что касается ошибки обслуживания аудиофаллинга, то, как вы заметили, она помечается как "чем == 100" или ошибкой (100,0).
Что вы можете сделать, чтобы избежать ошибки аудиофаллера из моего скромного опыта:
- Избегайте быстрых звонков на сервис (я добавляю 500 миллисекунд задержки после создания плеера)
- Ограничьте число одновременных активных медиаплееров.
Что вы можете сделать, чтобы справиться с ошибкой аудиофаллера:
- Обнаружите ошибку 100 аудиофиблера, установите флаг, в котором она произошла, и отключите графический интерфейс (рекомендуется только разблокировать проигрыватель, поскольку его остановка, когда он уже находится в состоянии ошибки, небезопасна и приведет к исключению IllegalStateException & error(38,0)
- Запустите еще один поток, который продолжает тестировать службу снова (возможно, путем создания нового медиаплеера без исключений) с тайм-аутом, скажем, 5-10 секунд.
- Когда служба вернется, сбросьте флаг и снова включите графический интерфейс
Итак, ссылаясь на ваш код:
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.release();
// here you add logic communicating the wrapper or main UI thread
// to disable GUI and set a flag
return false;
}
Затем вы добавляете метод для обработки этого в оболочке.
Я был бы очень признателен, когда вы решите это и опубликуете решение. Я тоже сталкиваюсь с очень похожей проблемой.
Если поток не может загрузить, вы часто застряли в состоянии подготовки, вы можете попробовать это здесь, когда mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR
:
mediaWrapper.reset();
mediaWrapper.release();
System.gc();
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
Лучше всего положить его в AsyncTask
, чтобы избежать не отвечая на ошибку. Или, когда вы получаете сообщение об ошибке, вы должны создать новый MediaPlayer, потому что Media Server умер:
if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR){
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
Если MediaPlayer
При воспроизведении потока вы должны сначала остановить и сбросить его:
mediaWrapper.stop();
mediaWrapper.reset();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
Это работает для меня, но я думаю, что это не лучший способ. Надеюсь, что кто-то может найти лучшее решение для того, что делать, когда вы застряли в состоянии подготовки.