如何将 Service 中的 ExoPlayer 与 activity 中的 ExoPlayerView 连接起来?
How to connect ExoPlayer inside Service with ExoPlayerView inside activity?
我想将 "Fullscreen" 按钮添加到 Google 的标准 SimpleExoPlayer 实现中。我添加了 ImageButton,到目前为止一切正常。
然后我决定全屏将以下一种方式工作:按下按钮后,我打开新的 activity 全屏 SimpleExoPlayerView,将视频 uri 和当前位置传递给那里,初始化播放器并寻找给定位置。它有效,但播放器重新初始化需要 1-3 秒,具体取决于不想要的设备。
现在我想要一个播放器 inside intent 服务实例,只需将现有播放器重新附加到任何想要显示它的视图(预览或全屏视图),如下所示:
mPlayerView.setPlayer(mPlayer);
问题是服务会有播放器,activity会有视图。他们中的任何一个都不能将播放器附加到视图。作为一种解决方法,我考虑让服务 class 对玩家有一个静态的 link,这样 activity 就可以通过静态引用来获取它。但这似乎有点代码味道,我不知道线程之间的通信是否会出现问题。
那么,如何将播放器从服务(不可序列化或可打包)传递到 Activity 或者如何将播放器视图传递到服务?
exoPlayer 可以很好地处理全屏 - 尝试更改 playerView 的布局参数以匹配旋转或按下按钮时的屏幕。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int width;
int height;
View decorView = getWindow().getDecorView();
int uiOptions;
if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, r.getDisplayMetrics());
width = CoordinatorLayout.LayoutParams.MATCH_PARENT;
height = (int)px;
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
} else {
uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
decorView.setSystemUiVisibility(uiOptions);
final CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(width, height);
mAppBar.setLayoutParams(lp);
}
另外不要忘记将此添加到您的 activity 清单中以防止 activity 重新加载:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
我在使用 MediaPlayer 播放视频时遇到了类似的问题。我已经使用 IBinder 绑定到 MediaBrowserService,然后获取了 MediaPlayer 实例。在您的服务中提供一种 returns 对 MediaPlayer 的引用的方法。像这样:
private MediaPlayer mediaPlayer;
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals("YOUR_INTENT")) {
return new LocalBinder();
}
return super.onBind(intent);
}
public class LocalBinder extends Binder{
public AudioService getService(){
return AudioService.this;
}
}
public MediaPlayer getMediaPlayer() {
return mediaPlayer;
}
然后在您的 Activity/Fragment 中使用 IBinder 绑定到 MediaBrowserService。在我的实现中,我使用了 MediaPlayer,但我认为它可以以类似的方式用于 Exoplayer。
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private boolean isServiceBounded;
private boolean isSurfaceReady;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isServiceBounded = true;
mediaPlayer = ((AudioService)service).getMediaPlayer();
if (isSurfaceReady) {
mediaPlayer.setDisplay(surfaceHolder);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isServiceBounded = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
bindService(new Intent("YOUR_INTENT"), serviceConnection, BIND_AUTO_CREATE);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceHolder = holder;
isSurfaceReady = true;
if (mediaPlayer != null) {
mediaPlayer.setDisplay(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isSurfaceReady = false;
if (mediaPlayer != null) {
mediaPlayer.setDisplay(null);
}
surfaceHolder = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
我想将 "Fullscreen" 按钮添加到 Google 的标准 SimpleExoPlayer 实现中。我添加了 ImageButton,到目前为止一切正常。
然后我决定全屏将以下一种方式工作:按下按钮后,我打开新的 activity 全屏 SimpleExoPlayerView,将视频 uri 和当前位置传递给那里,初始化播放器并寻找给定位置。它有效,但播放器重新初始化需要 1-3 秒,具体取决于不想要的设备。
现在我想要一个播放器 inside intent 服务实例,只需将现有播放器重新附加到任何想要显示它的视图(预览或全屏视图),如下所示:
mPlayerView.setPlayer(mPlayer);
问题是服务会有播放器,activity会有视图。他们中的任何一个都不能将播放器附加到视图。作为一种解决方法,我考虑让服务 class 对玩家有一个静态的 link,这样 activity 就可以通过静态引用来获取它。但这似乎有点代码味道,我不知道线程之间的通信是否会出现问题。
那么,如何将播放器从服务(不可序列化或可打包)传递到 Activity 或者如何将播放器视图传递到服务?
exoPlayer 可以很好地处理全屏 - 尝试更改 playerView 的布局参数以匹配旋转或按下按钮时的屏幕。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int width;
int height;
View decorView = getWindow().getDecorView();
int uiOptions;
if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, r.getDisplayMetrics());
width = CoordinatorLayout.LayoutParams.MATCH_PARENT;
height = (int)px;
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
} else {
uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
decorView.setSystemUiVisibility(uiOptions);
final CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(width, height);
mAppBar.setLayoutParams(lp);
}
另外不要忘记将此添加到您的 activity 清单中以防止 activity 重新加载:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
我在使用 MediaPlayer 播放视频时遇到了类似的问题。我已经使用 IBinder 绑定到 MediaBrowserService,然后获取了 MediaPlayer 实例。在您的服务中提供一种 returns 对 MediaPlayer 的引用的方法。像这样:
private MediaPlayer mediaPlayer;
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals("YOUR_INTENT")) {
return new LocalBinder();
}
return super.onBind(intent);
}
public class LocalBinder extends Binder{
public AudioService getService(){
return AudioService.this;
}
}
public MediaPlayer getMediaPlayer() {
return mediaPlayer;
}
然后在您的 Activity/Fragment 中使用 IBinder 绑定到 MediaBrowserService。在我的实现中,我使用了 MediaPlayer,但我认为它可以以类似的方式用于 Exoplayer。
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private boolean isServiceBounded;
private boolean isSurfaceReady;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isServiceBounded = true;
mediaPlayer = ((AudioService)service).getMediaPlayer();
if (isSurfaceReady) {
mediaPlayer.setDisplay(surfaceHolder);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isServiceBounded = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
bindService(new Intent("YOUR_INTENT"), serviceConnection, BIND_AUTO_CREATE);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceHolder = holder;
isSurfaceReady = true;
if (mediaPlayer != null) {
mediaPlayer.setDisplay(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isSurfaceReady = false;
if (mediaPlayer != null) {
mediaPlayer.setDisplay(null);
}
surfaceHolder = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}