一、背景
Android智能座舱,视频播放场景,通过多指滑屏退回桌面,会出现闪屏问题。
二、问题定位以及修改方案
通过录屏,然后一帧一帧播放的方式,发现闪屏是因为,多指的情况下,不仅触发了退回桌面的动作,还触发了调节视频app的亮度,以及视频的快进和快退的动作,几种UI刷新叠加起来,造成了闪屏的现象。
除了调整手势识别的方法解决闪屏的问题,还有一种方式,就是退出页面的时候截屏,卡住UI的刷新,也能解决闪屏的问题。
1. 步骤一
通过如下函数takeScreenshot,截取主屏的bitmap,但是通过SurfaceControl.captureDisplay获取到的bitmap对应的buffer是SurfaceControl.ScreenshotHardwareBuffer,属于hadware bitmaps,无法通过software rendering,会报如下错误:
Software rendering doesn’t support hardware bitmaps
必须通过如下方式,将hardware bitmap转成soft bitmap:
Bitmap softwareBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, true);
private Bitmap takeScreenshot(int displayId) {if (displayId != 0) {return null;}// Take the screenshotfinal IBinder displayToken = SurfaceControl.getInternalDisplayToken();final SurfaceControl.DisplayCaptureArgs captureArgs =new SurfaceControl.DisplayCaptureArgs.Builder(displayToken).build();final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =SurfaceControl.captureDisplay(captureArgs);Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();if (screenShot == null) {Log.e(TAG, "Failed to take fullscreen screenshot");return null;}// OptimizationscreenShot.setHasAlpha(false);Bitmap softwareBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, true);if (screenShot != null) {screenShot.recycle();screenShot = null;}return softwareBitmap;}
2.步骤二
通过函数createScreenshotLayer,将takeScreenshot 获取到的software bitmap,生成对应的SurfaceControl ,也就是底层的Layer数据。
private SurfaceControl createScreenshotLayer(Bitmap bitmap) {Log.w(TAG, "launchHomeFromFivePointerCatch: createScreenshotLayer bitmap : " + bitmap + ", w = " + bitmap.getWidth()+ " ,h = " + bitmap.getHeight());// 创建 SurfaceControl 图层SurfaceControl surfaceControl = new SurfaceControl.Builder().setName("ScreenshotLayer").setBufferSize(bitmap.getWidth(), bitmap.getHeight()).setFormat(PixelFormat.TRANSLUCENT).build();Surface surface = new Surface(surfaceControl);Canvas canvas = surface.lockCanvas(null);canvas.drawBitmap(bitmap, 0, 0, null);surface.unlockCanvasAndPost(canvas);return surfaceControl;}
3. 步骤三
通过playExitAnimation实现冻屏或者刷帧的动作,最后测试发现,冻屏比较适合,也相对简单。刷帧的方式,因为不能很好的掌握UI的调整和显示的时间,无法正确确定刷帧的起始和结束的时机。
private void playExitAnimation(SurfaceControl surfaceControl) {Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation surfaceControl : " + surfaceControl);SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();方案1:冻屏/*// 设置动画属性/transaction.setMatrix(surfaceControl, 0.5f, 0, 0, 0.5f); // 缩放到 50%/transaction.setAlpha(surfaceControl, 0.0f); // 设置透明度为 0transaction.setLayer(surfaceControl, Integer.MAX_VALUE);transaction.show(surfaceControl);transaction.apply();// 延时销毁图层mHandler.postDelayed(() -> {transaction.remove(surfaceControl).apply();}, 500); // 动画持续 500ms*/float startScale = 1.0f;float endScale = 0.5f;float startAlpha = 1.0f;float endAlpha = 0.0f;int duration = 500;long startTime = System.currentTimeMillis();方案2:通过handler 刷帧/*mHandler.post(new Runnable() {@Overridepublic void run() {if (!surfaceControl.isValid()) {return;}long elapsedTime = System.currentTimeMillis() - startTime;if (elapsedTime > duration) return;float progress = (float) elapsedTime / duration;float currentScale = startScale + progress * (endScale - startScale);float currentAlpha = startAlpha + progress * (endAlpha - startAlpha);Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation progress : -----------" + progress+ ", currentScale = " + currentScale + ", currentAlpha = " + currentAlpha);//SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();transaction.setMatrix(surfaceControl, currentScale, 0, 0, currentScale); // 设置缩放transaction.setAlpha(surfaceControl, currentAlpha); // 设置透明度transaction.setLayer(surfaceControl, Integer.MAX_VALUE);transaction.show(surfaceControl);transaction.apply();mHandler.post(this);}});*/方案3:通过Choreographer刷帧Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {long startTime = System.nanoTime();@Overridepublic void doFrame(long frameTimeNanos) {if (!surfaceControl.isValid()) {return;}float progress = (frameTimeNanos - startTime) / (float) (duration * 1_000_000); // 计算进度if (progress > 1.0f) return;float currentScale = startScale + progress * (endScale - startScale);float currentAlpha = startAlpha + progress * (endAlpha - startAlpha);Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation progress : -----------" + progress+ ", currentScale = " + currentScale + ", currentAlpha = " + currentAlpha);//transaction.setMatrix(surfaceControl, currentScale, 0, 0, currentScale); // 设置缩放transaction.setAlpha(surfaceControl, currentAlpha); // 设置透明度transaction.setLayer(surfaceControl, Integer.MAX_VALUE);transaction.show(surfaceControl);transaction.apply();Choreographer.getInstance().postFrameCallback(this); // 下一帧}});mHandler.postDelayed(() -> {//SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();transaction.hide(surfaceControl); // 隐藏图层transaction.remove(surfaceControl); // 移除图层transaction.apply();surfaceControl.release(); // 释放资源}, duration);}