将 PlayerView 与来自服务的 SimpleExoPlayer 绑定 [英] Binding PlayerView with SimpleExoPlayer from a service

查看:15
本文介绍了将 PlayerView 与来自服务的 SimpleExoPlayer 绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了一个在后台运行音频的服务,该服务运行良好,但我无法从服务获取 SimpleExoPlayer 的实例到活动以更新 UI,如果我退出,音频也会在后台播放两次并重新打开活动.

I have implemented a Service to run the audio in background which runs perfectly but I am unable to get the instance of the SimpleExoPlayer from the service to the activity to update the UI and also the Audio plays twice in the background if I exit and reopen the activity.

AudioPlayerService

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 对象,如果我不使用 Intent 传递数据,服务将崩溃,因此我无法在服务本身中执行 startService() 我必须在活动中开始我猜.

This is my activity where I am starting the service and binding to it. I have to pass the Item object in-order the service to run, if I don't pass the data using the intent the service will crash so I can't do startService() in the service itself I have to start in the activity I guess.

玩家活动

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);
    }
}

我已经尝试了我所知道的一切,但因此我无法继续前进.

I have tried everything I know but I'm unable to move forward because of this.

推荐答案

因此,经过大量研究后,我能够使用绑定服务并从该服务获取 SimpleExoPlayer 实例并将播放器视图设置为始终显示来解决此问题使用以下方法.

So after a lot of research I was able to resolve this using the bound service and getting the SimpleExoPlayer instance from the service and set the Player view to always show up using the following method.

mPlayerView.showController()

经过所有修改和设置后,只需要两个类就可以实现带通知控制的后台音频播放,一个是活动,另一个是使用最新exoplayer版本的服务.

After all the modification and setup it takes only two classes to achieve background audio playback with notification control, one is the activity and the other is the service using the latest exoplayer release.

玩家活动

    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 + "

" + 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);
    }
}

AudioPlayerService

    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;
        }
    }
}

这篇关于将 PlayerView 与来自服务的 SimpleExoPlayer 绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆