音楽ストリーミングアプリを作成しています。Android の MediaPlayer ガイドに従って、サービスから MediaPlayer を制御しています。これはすべて正常に機能し、再生を制御するために MediaController を追加しようとしています。そのために、Service に MediaController.MediaPlayerControl を実装させ、Activity を Service にバインドしてから、ServiceConnection の Service コンテキストを使用して Activity から MediaController をインスタンス化します。
Player.java
public class Player extends Activity implements OnClickListener, OnItemClickListener, MediaController.MediaPlayerControl {
private MediaController mediaController;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
showMediaController();
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
startService(
new Intent(this, PlayerService.class)
.setAction("com.limastreamer.action.NEXTSHOW"));
bindService(
new Intent(this, PlayerService.class),
mConnection,
Context.BIND_AUTO_CREATE);
}
public void showMediaController() {
if (mBound) {
mediaController = new MediaController(this);
mediaController.setAnchorView(
findViewById(R.id.player)
);
mediaController.setMediaPlayer(mService);
mediaController.setEnabled(true);
mediaController.show(0);
}
}
}
PlayerService.java
public class PlayerService extends Service implements MediaController.MediaPlayerControl {
private MediaPlayer mMediaPlayer;
public class LocalBinder extends Binder {
PlayerService getService() {
return PlayerService.this;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String action = intent.getAction();
if (action.equals("com.limastreamer.action.NEXTSHOW")) {
if (mMediaPlayer == null)
{
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setLooping(false);
}
try
{
mMediaPlayer.reset();
mMediaPlayer.setDataSource(url);
mMediaPlayer.prepareAsync(); // prepare async to not block main thread
}
catch (Exception ex)
{
Toast.makeText(getApplicationContext(), "Failed to prepare MediaPlayer", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public boolean canPause() {
return true;
}
@Override
public boolean canSeekBackward() {
return true;
}
@Override
public boolean canSeekForward() {
return true;
}
@Override
public int getBufferPercentage() {
return 0;
}
@Override
public int getCurrentPosition() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying())
return mMediaPlayer.getCurrentPosition();
else
return 0;
}
@Override
public int getDuration() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying())
return mMediaPlayer.getDuration();
else
return 0;
}
@Override
public boolean isPlaying() {
if (mMediaPlayer != null)
return mMediaPlayer.isPlaying();
else
return false;
}
@Override
public void pause() {
if (mMediaPlayer != null)
mMediaPlayer.pause();
}
@Override
public void seekTo(int msec) {
if (mMediaPlayer != null)
mMediaPlayer.seekTo(msec);
}
@Override
public void start() {
if (mMediaPlayer != null)
mMediaPlayer.start();
}
}
R.id.player は、私の xml レイアウトのルート要素を参照します。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".Player" >
mediaController.show();
例外を除いて、アプリの爆弾を呼び出すと:Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
SO に関する他の質問 (たとえば) を見ると、これはここで間違ったコンテキストを使用していることが原因のようです:mediaController = new MediaController(this);
つまり、Activity コンテキスト以外のものを使用しています。しかし、私が知る限り、Activity コンテキストを使用しています。
私はもう試した:
- レイアウト内の他のビューをアンカー ビューとして使用する (アクティビティのメイン ビューを使用できるとドキュメントに記載されていても)
- ここに示すように、MediaController をフラグメントに入れ、コンテキストとして getActivity() を使用する
- プログラムでインスタンス化する代わりに、MediaController を xml レイアウトに配置します。
- VideoView をアンカー ビューとして設定します (VideoView でのみ機能すると言う人もいます)。
- VideoView を拡張して MediaPlayerControl を実装する新しいクラスを作成し、そのクラスで MediaController をインスタンス化し、初期化時にクラスに渡された保存済みコンテキストをコンテキストおよび
this
アンカー ビューとして使用します。