我已经实现了一项服务来在后台运行运行完美运行的音频,但我无法将 SimpleExoPlayer 的实例从服务获取到活动以更新 UI,如果我退出并重新打开活动,音频也会在后台播放两次。
音频播放器服务
public class AudioPlayerService extends Service {
private final IBinder mBinder = new LocalBinder();
private SimpleExoPlayer player;
private Item item;
private PlayerNotificationManager playerNotificationManager;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
playerNotificationManager.setPlayer(null);
player.release();
player = null;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
enter code here
public SimpleExoPlayer getplayerInstance() {
return player;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle b = intent.getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
item = b.getParcelable(AppConstants.ITEM_KEY);
}
startPlayer();
return START_STICKY;
}
private void startPlayer() {
final Context context = this;
Uri uri = Uri.parse(item.getUrl());
player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, getString(R.string.app_name)));
MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri);
player.prepare(mediaSource);
player.setPlayWhenReady(true);
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(context, AppConstants.PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
AppConstants.PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
@Override
public String getCurrentContentTitle(Player player) {
return item.getTitle();
}
@Nullable
@Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(context, PlayerActivity.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Nullable
@Override
public String getCurrentContentText(Player player) {
return item.getSummary();
}
@Nullable
@Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return item.getBitmap();
}
}
);
playerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
@Override
public void onNotificationStarted(int notificationId, Notification notification) {
startForeground(notificationId, notification);
}
@Override
public void onNotificationCancelled(int notificationId) {
stopSelf();
}
});
playerNotificationManager.setPlayer(player);
}
public class LocalBinder extends Binder {
public AudioPlayerService getService() {
return AudioPlayerService.this;
}
}
}
这是我的活动,我在其中启动服务并绑定到它。我必须传递 Item 对象才能运行服务,如果我不使用意图传递数据,服务将崩溃,所以我无法在服务本身中执行 startService((,我必须在我猜的活动中启动。
玩家活动
public class PlayerActivity extends BaseActivity {
@BindView(R.id.video_view)
PlayerView mPlayerView;
@BindView(R.id.tvTitle)
TextView mTvTitle;
@BindView(R.id.tvSummary)
TextView mTvSummary;
@BindView(R.id.ivThumbnail)
ImageView mIvThumb;
private SimpleExoPlayer player;
private String mURL, mTitle, mSummary, mImage;
private AudioPlayerService mService;
private boolean mBound = false;
private Intent intent;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AudioPlayerService.LocalBinder binder = (AudioPlayerService.LocalBinder) iBinder;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
@SuppressLint("MissingSuperCall")
@Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.activity_player);
Bundle b = getIntent().getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
Item item = b.getParcelable(AppConstants.ITEM_KEY);
mURL = item.getUrl();
mImage = item.getImage();
mTitle = item.getTitle();
mSummary = item.getSummary();
intent = new Intent(this, AudioPlayerService.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
Util.startForegroundService(this, intent);
}
}
private void initializePlayer() {
if (player == null && !mURL.isEmpty() && mBound) {
player = mService.getplayerInstance();
mPlayerView.setPlayer(player);
mPlayerView.setControllerHideOnTouch(false);
mPlayerView.setControllerShowTimeoutMs(10800000);
}
}
@Override
public void onStart() {
super.onStart();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
setUI();
}
private void setUI() {
mTvTitle.setText(mTitle);
mTvSummary.setText(mSummary);
GlideApp.with(this)
.load(mImage)
.placeholder(R.color.colorPrimary)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mIvThumb);
}
@Override
protected void onStop() {
unbindService(mConnection);
mBound = false;
releasePlayer();
super.onStop();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.player_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_podcast:
//Logic for Share
return true;
case R.id.download_podcast:
//Logic for download
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
@Override
public void onToolBarSetUp(Toolbar toolbar, ActionBar actionBar) {
TextView tvHeader = toolbar.findViewById(R.id.tvClassName);
tvHeader.setText(R.string.app_name);
actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow_back_black_24dp);
}
}
我已经尝试了我所知道的一切,但因此我无法前进。
因此,经过大量研究,我能够使用绑定服务并从服务中获取 SimpleExoPlayer 实例来解决此问题,并将播放器视图设置为始终使用以下方法显示。
mPlayerView.showController()
完成所有修改和设置后,只需两个类即可通过通知控制实现后台音频播放,一个是活动,另一个是使用最新 exoplayer 版本的服务。
玩家活动
public class PlayerActivity extends BaseActivity {
@BindView(R.id.video_view)
PlayerView mPlayerView;
@BindView(R.id.tvTitle)
TextView mTvTitle;
@BindView(R.id.tvSummary)
TextView mTvSummary;
@BindView(R.id.ivThumbnail)
ImageView mIvThumb;
private String mUrl, mTitle, mSummary, mImage;
private AudioPlayerService mService;
private Intent intent;
private String shareableLink;
private boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AudioPlayerService.LocalBinder binder = (AudioPlayerService.LocalBinder) iBinder;
mService = binder.getService();
mBound = true;
initializePlayer();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
@SuppressLint("MissingSuperCall")
@Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.activity_player);
Bundle b = getIntent().getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
Item item = b.getParcelable(AppConstants.ITEM_KEY);
shareableLink = b.getString(AppConstants.SHARE_KEY);
mImage = item.getImage();
mUrl = item.getUrl();
mTitle = item.getTitle();
mSummary = item.getSummary();
intent = new Intent(this, AudioPlayerService.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
Util.startForegroundService(this, intent);
mPlayerView.setUseController(true);
mPlayerView.showController();
mPlayerView.setControllerAutoShow(true);
mPlayerView.setControllerHideOnTouch(false);
}
}
private void initializePlayer() {
if (mBound) {
SimpleExoPlayer player = mService.getplayerInstance();
mPlayerView.setPlayer(player);
}
}
@Override
public void onStart() {
super.onStart();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
setUI();
}
private void setUI() {
mTvTitle.setText(mTitle);
mTvSummary.setText(mSummary);
GlideApp.with(this)
.load(mImage)
.placeholder(R.color.colorPrimary)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mIvThumb);
}
@Override
protected void onStop() {
unbindService(mConnection);
mBound = false;
super.onStop();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.player_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_podcast:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, mTitle);
shareIntent.putExtra(Intent.EXTRA_TEXT, mTitle + "nn" + shareableLink);
shareIntent.setType("text/plain");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_text)));
return true;
case R.id.download_podcast:
Uri uri = Uri.parse(mUrl);
ProgressiveDownloadAction progressiveDownloadAction
= new ProgressiveDownloadAction(uri, false, null, null);
AudioDownloadService.startWithAction(PlayerActivity.this, AudioDownloadService.class,
progressiveDownloadAction, false);
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onToolBarSetUp(Toolbar toolbar, ActionBar actionBar) {
TextView tvHeader = toolbar.findViewById(R.id.tvClassName);
tvHeader.setText(R.string.app_name);
actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow_back_black_24dp);
}
}
音频播放器服务
public class AudioPlayerService extends Service {
private final IBinder mBinder = new LocalBinder();
private SimpleExoPlayer player;
private Item item;
private PlayerNotificationManager playerNotificationManager;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
releasePlayer();
super.onDestroy();
}
private void releasePlayer() {
if (player != null) {
playerNotificationManager.setPlayer(null);
player.release();
player = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SimpleExoPlayer getplayerInstance() {
if (player == null) {
startPlayer();
}
return player;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//releasePlayer();
Bundle b = intent.getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
item = b.getParcelable(AppConstants.ITEM_KEY);
}
if (player == null) {
startPlayer();
}
return START_STICKY;
}
private void startPlayer() {
final Context context = this;
Uri uri = Uri.parse(item.getUrl());
player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, getString(R.string.app_name)));
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(
DownloadUtil.getCache(context),
dataSourceFactory,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
MediaSource mediaSource = new ExtractorMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri);
player.prepare(mediaSource);
player.setPlayWhenReady(true);
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(context, AppConstants.PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
AppConstants.PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
@Override
public String getCurrentContentTitle(Player player) {
return item.getTitle();
}
@Nullable
@Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(context, PlayerActivity.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Nullable
@Override
public String getCurrentContentText(Player player) {
return item.getSummary();
}
@Nullable
@Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
}
);
playerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
@Override
public void onNotificationStarted(int notificationId, Notification notification) {
startForeground(notificationId, notification);
}
@Override
public void onNotificationCancelled(int notificationId) {
stopSelf();
}
});
playerNotificationManager.setPlayer(player);
}
public class LocalBinder extends Binder {
public AudioPlayerService getService() {
return AudioPlayerService.this;
}
}
}