android相机热启动缓存帧解决方案(任务快照)

摘要:android APP的启动方式主要分为两种:冷启动和热启动。冷启动会把LOGO当作启动页面;热启动则会把任务快照当作启动页面。相机作为实时获取预览图像的应用,在热启动时就会存在一个问题:前一次退出的预览画面会被存储为任务快照,下一次热启动时,会先显示之前退出时被保存的任务快照,当预览准备好后,刷新预览数据,这就会有一种跳帧的现象。本文通过目前行业机的通用处理方式,将相机的任务快照进行蒙层虚化(模糊)处理,提升用户体验感。

一、准备知识

android冷启动为什么会自动把LOGO当做启动页面

应用启动时间(android developers)

任务快照(android source)

二、代码实现

目标平台:MTK android 13

方案:在获取任务快照snapshotTask中,填充buffer之前将获取到的buffer内容进行模糊处理。

java 复制代码
Index: frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java
===================================================================
--- frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java	(revision B)
+++ frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java	(revision A)
@@ -38,13 +38,16 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorSpace;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderEffect;
 import android.graphics.RenderNode;
 import android.hardware.HardwareBuffer;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.util.ArraySet;
@@ -512,11 +515,73 @@
             // Failed to acquire image. Has been logged.
             return null;
         }
-        builder.setSnapshot(screenshotBuffer.getHardwareBuffer());
+        String targetPackage = task.getTopMostActivity().mActivityComponent.getPackageName();
+        if ("com.mediatek.camera".equals(targetPackage)) {
+            Slog.d("TaskSnapshotController", "snapshotTask::mtk cam snapshot");
+            builder.setSnapshot(applyBlurToBuffer(screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace()));
+        } else {
+            builder.setSnapshot(screenshotBuffer.getHardwareBuffer());
+        }
         builder.setColorSpace(screenshotBuffer.getColorSpace());
         return builder.build();
     }
 
+    private HardwareBuffer applyBlurToBuffer(HardwareBuffer originalBuffer,
+                                             ColorSpace colorSpace) {
+        // Only RGBA format is supported
+        if (originalBuffer.getFormat() != HardwareBuffer.RGBA_8888 &&
+            originalBuffer.getFormat() != HardwareBuffer.RGBA_FP16) {
+            Slog.w("TaskSnapshotController", "Unsupported buffer format for blur: " + originalBuffer.getFormat());
+            return originalBuffer;
+        }
+
+        int width = originalBuffer.getWidth();
+        int height = originalBuffer.getHeight();
+        if (width <= 1 || height <= 1) return originalBuffer;
+
+        // Packaging the original HardwareBuffer as a Bitmap (without copying pixels)
+        Bitmap srcBitmap = Bitmap.wrapHardwareBuffer(originalBuffer, colorSpace);
+        if (srcBitmap == null) return originalBuffer;
+
+        try {
+            RenderNode node = RenderNode.create("BlurLayer", null);
+            node.setLeftTopRightBottom(0, 0, width, height);
+            RecordingCanvas canvas = node.start(width, height);
+
+            // Draw the original image
+            canvas.drawBitmap(srcBitmap, 0, 0, null);
+
+            // Apply Gaussian blur effect (API 31+)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                RenderEffect blurEffect = RenderEffect.createBlurEffect(
+                        25f, 25f, android.graphics.Shader.TileMode.CLAMP);
+                node.setRenderEffect(blurEffect);
+            } else {
+                // The low version cannot be blurred, and the original buffer is returned directly
+                srcBitmap.recycle();
+                return originalBuffer;
+            }
+
+            node.end(canvas);
+
+            // Generate new HardwareBuffer
+            Bitmap blurredBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+            srcBitmap.recycle(); // Release the temporary Bitmap (without closing the original buffer)
+
+            if (blurredBitmap == null) {
+                return originalBuffer;
+            }
+
+            HardwareBuffer blurredBuffer = blurredBitmap.getHardwareBuffer();
+            blurredBitmap.recycle(); // The Bitmap wrapper is released, but its internal buffer is still referenced
+            return blurredBuffer != null ? blurredBuffer : originalBuffer;
+
+        } catch (Exception e) {
+            Slog.e("TaskSnapshotController", "Failed to apply blur to buffer", e);
+            return originalBuffer;
+        }
+    }
+
     void setTaskSnapshotEnabled(boolean enabled) {
         mTaskSnapshotEnabled = enabled;
     }
相关推荐
flower_drop5 小时前
基于 WebUSB 与 CDP:在浏览器端实现 Android 设备通信与无证书抓包实践
android·chrome·测试工具·adb·bug·edge浏览器
撩得Android一次心动7 小时前
Jetpack Compose 知识点整理1【个人用】
android·学习·kotlin·android jetpack·compose
ForteScarlet8 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
小刘在重生~9 小时前
Java 常用类|包装类 装箱拆箱、Integer 缓存面试题
java·python·缓存
方白羽9 小时前
Android Studio 自动国际化插件:SmartI18n
android·android studio·intellij idea
码农coding12 小时前
android12 状态栏图标的加载流程
android
hai_android13 小时前
Kotlin 协程作用域源码剖析
android·kotlin
Yeyu13 小时前
Android 画中画(PiP)小窗播放:怎么做、以及它到底是怎么工作的
android
千里马学框架13 小时前
安卓系统性能优化高级实战开发专题--开机,app冷启动优化
android·智能手机·性能优化·framework·性能·开机优化·app冷启动优化
律宏阔14 小时前
Android 手机通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板
android