Android智能座舱,视频播放场景,通过多指滑屏退回桌面,闪屏问题的另一种解法

一、背景

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

java 复制代码
    private Bitmap takeScreenshot(int displayId) {
        if (displayId != 0) {
            return null;
        }
        // Take the screenshot
        final 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;
        }

        // Optimization
        screenShot.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数据。

java 复制代码
    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的调整和显示的时间,无法正确确定刷帧的起始和结束的时机。

java 复制代码
    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); // 设置透明度为 0
        transaction.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() {
            @Override
            public 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();
            @Override
            public 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);



    }
相关推荐
szhangbiao1 小时前
“开发板”类APP如果做屏幕适配
android
高林雨露2 小时前
RecyclerView中跳转到最后一条item并确保它在可视区域内显示
android
梦子要转行4 小时前
matlab/Simulink-全套50个汽车性能建模与仿真源码模型9
开发语言·matlab·汽车
移动开发者1号4 小时前
ReLinker优化So库加载指南
android·kotlin
山野万里__4 小时前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
Huckings4 小时前
Android 性能问题
android
移动开发者1号5 小时前
剖析 Systrace:定位 UI 线程阻塞的终极指南
android·kotlin
移动开发者1号5 小时前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
whysqwhw5 小时前
OkHttp深度架构缺陷分析与革命性演进方案
android
Digitally7 小时前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone