浅谈Android 原生 Picture in Picture 画中画功能的“坑”与规避之道
2023-04-21 03:31:04
Android 画中画:潜藏的陷阱与化解之道
画中画(Picture in Picture,PiP)是一项强大的 Android 功能,它允许应用程序在其他任务运行时以小型悬浮窗口的形式显示。该功能于 Android 8.0 中引入,旨在提升多任务处理效率。然而,原生画中画功能却存在诸多限制和问题,导致开发者在使用时不得不小心应对。
原生画中画的限制
1. 任务栈问题
原生画中画功能创建新的任务栈,可能引发意外行为。当用户在画中画模式下执行任务时,系统会将该任务放入新任务栈。此时,如果用户按下 Home 键,系统将返回到前一个应用程序,而不是退出画中画模式。
2. 兼容性问题
并非所有设备都支持原生画中画功能。即使在支持的设备上,也可能出现兼容性问题,如画中画窗口闪烁或冻结。
3. 性能问题
原生画中画功能可能导致性能问题,尤其当画中画窗口包含大量动画或复杂元素。这是因为系统需要不断后台渲染画中画窗口,消耗大量系统资源。
化解陷阱之法
1. 慎重考虑是否使用原生画中画功能
在使用原生画中画功能之前,开发者需要认真思考是否有必要使用该功能。如果仅仅是为了实现简单的悬浮窗口效果,可以考虑使用自定义浮窗,避免原生画中画功能的诸多问题。
2. 正确处理任务栈
若决定使用原生画中画功能,务必正确处理任务栈问题。可以使用以下代码检查当前是否处于画中画模式:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (isInPictureInPictureMode()) {
// 处于画中画模式
} else {
// 不处于画中画模式
}
}
如果处于画中画模式,用户按下 Home 键时,需要将画中画窗口移至前台。可以使用以下代码实现:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (isInPictureInPictureMode()) {
moveTaskToFront(taskId, 0);
}
}
3. 优化性能
若画中画窗口包含大量动画或复杂元素,需要优化性能。可以采取以下措施:
- 减少动画使用
- 减少复杂元素使用
- 使用硬件加速
- 使用高效渲染技术
自定义浮窗实现画中画功能
若原生画中画功能无法满足特定需求或避免其问题,开发者可以使用自定义浮窗实现画中画功能。自定义浮窗的实现步骤如下:
- 创建悬浮窗口布局文件
- 创建悬浮窗口服务类
- 在服务类中启动悬浮窗口
- 在悬浮窗口布局文件中添加控件
- 在悬浮窗口服务类中处理控件事件
结语
原生 Android 画中画功能虽然强大,但存在局限性和问题。开发者在使用时应格外注意。如果可以接受应用程序有两个任务栈,可以使用原生画中画功能;否则,建议使用自定义浮窗实现画中画功能。
常见问题解答
1. 如何检查设备是否支持画中画功能?
可以使用以下代码检查设备是否支持画中画功能:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
boolean supportsPictureInPicture = getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
}
2. 如何在自定义浮窗中实现画中画功能?
可以遵循本文中提到的步骤,使用自定义浮窗实现画中画功能。
3. 如何在画中画窗口中播放视频?
使用原生画中画功能播放视频,可以参考以下代码:
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(AspectRatio.of(16, 9))
.build();
enterPictureInPictureMode(params);
使用自定义浮窗播放视频,可以参考以下代码:
// 创建 MediaPlayer
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.video);
// 设置 MediaPlayer 的位置和大小
mediaPlayer.setDisplay(holder.getSurfaceHolder());
mediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
// 开始播放视频
mediaPlayer.start();
4. 如何在画中画窗口中显示 Web 内容?
可以使用 WebView 来在画中画窗口中显示 Web 内容。可以参考以下代码:
// 创建 WebView
WebView webView = new WebView(context);
webView.loadUrl("https://www.example.com");
// 将 WebView 添加到画中画窗口的布局中
ViewGroup layout = (ViewGroup) findViewById(R.id.pip_layout);
layout.addView(webView);
5. 如何在画中画窗口中使用摄像头预览?
可以使用 Camera2 API 在画中画窗口中使用摄像头预览。可以参考以下代码:
// 创建 CameraManager
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
// 获取后置摄像头 ID
String cameraId = cameraManager.getCameraIdList()[0];
// 创建 CameraCaptureSession
CameraCaptureSession cameraCaptureSession;
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
SurfaceTexture surfaceTexture = new SurfaceTexture(0);
Surface surface = new Surface(surfaceTexture);
// 创建 CameraCaptureRequest
CameraCaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder.addTarget(surface);
// 创建 CameraCaptureSession
cameraCaptureSession = cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
cameraCaptureSession.setRepeatingRequest(requestBuilder.build(), null, null);
}
}, null);
}
}, null);
// 设置预览 SurfaceTexture
surfaceTexture.setDefaultBufferSize(1920, 1080);
SurfaceTextureListener surfaceTextureListener = new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
// 将 SurfaceTexture 设置为画中画窗口的预览窗口
ViewGroup layout = (ViewGroup) findViewById(R.id.pip_layout);
TextureView textureView = new TextureView(context);
textureView.setSurfaceTexture(surfaceTexture);
layout.addView(textureView);
}
};
surfaceTexture.setOnFrameAvailableListener(surfaceTextureListener, null);