CameraManager初始化优化:懒加载、预创建与权限检查精简

相机启动慢,很多时候不是硬件不行,而是初始化阶段埋了坑。我见过太多团队在Application.onCreate里直接调CameraManager.getInstance(),结果呢?启动时间硬生生多了几十毫秒。今天咱们就聊聊CameraManager初始化这件事。

一、CameraManager初始化的本质

CameraManager是Android相机框架的门面。说白了,它就是个系统服务的代理。你调用getInstance()时,它内部会去获取CameraService的binder引用。这个过程涉及跨进程通信,虽然不算重,但架不住它会在主线程执行。

我习惯把CameraManager的初始化分成三个阶段:

阶段 操作 耗时(参考)
获取服务 通过ServiceManager获取CameraService 5-15ms
权限检查 检查调用方是否有相机权限 2-8ms
创建实例 构造CameraManager对象 1-3ms

你看,加起来也就十几毫秒。但问题是,很多App在启动阶段会多次调用getInstance(),每次都要走一遍这个流程。积少成多,启动时间就这么被吃掉了。

二、懒加载:别在Application里初始化

我见过最典型的错误写法:

// 错误示范
public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        // 保存到全局变量...
    }
}

为什么不要这么做?因为用户可能根本不需要相机。你想想看,一个阅读类App,用户打开只是为了看书,你却提前初始化了相机服务。这不是浪费吗?

正确的做法是:

public class CameraManagerHolder {
    private static volatile CameraManager sInstance;
    
    public static CameraManager getInstance(Context context) {
        if (sInstance == null) {
            synchronized (CameraManagerHolder.class) {
                if (sInstance == null) {
                    sInstance = (CameraManager) context.getApplicationContext()
                            .getSystemService(Context.CAMERA_SERVICE);
                }
            }
        }
        return sInstance;
    }
}

嗯,这里要注意:一定要用ApplicationContext,避免Activity泄漏。我踩过这个坑,有一次没注意传了Activity的Context,结果相机页面退出了,CameraManager还持有Activity引用,内存泄漏了。

三、预创建实例:时机很重要

懒加载虽然好,但有个问题:用户第一次打开相机时,还是要等那十几毫秒。对于追求极致启动速度的场景,我们可以考虑预创建。

预创建的核心思路是:在用户大概率会使用相机的时机,提前初始化CameraManager。比如:

  • 用户进入相机相关的页面时
  • 用户点击了「拍照」按钮的前一个页面
  • App后台切换到前台时(如果相机是核心功能)

关键点:预创建不是越早越好,而是在「用户即将使用相机」的时机做。我建议用IdleHandler来触发预创建,避免影响当前页面的渲染。

// 在合适的时机触发预创建
Looper.myQueue().addIdleHandler(() -> {
    CameraManagerHolder.getInstance(context);
    return false; // 只执行一次
});

为什么用IdleHandler?因为它在主线程空闲时才执行。不会抢当前页面的渲染资源,也不会让用户感觉到卡顿。

四、减少权限检查次数

CameraManager初始化时,系统会检查调用方是否有相机权限。这个检查虽然快,但架不住频繁调用。我遇到过一个问题:某个页面在onResume里每次都重新获取CameraManager,结果权限检查被反复执行。

优化方案其实很简单:

  1. 缓存实例:全局单例,只初始化一次
  2. 权限预检查:在初始化前先确认权限状态
  3. 避免重复获取:不要在循环或频繁回调中调用getInstance()

小技巧:如果你的App在Android 10+上运行,可以试试用CameraManager的getCameraIdList()来触发初始化。这个方法内部会做权限检查,但比getInstance()更轻量。不过要注意,这个方法在Android 9及以下版本可能表现不同。

五、整体优化策略

我把这些优化点整理成一个流程图,方便你理解整体策略:

CameraManager初始化优化策略 应用启动 用户是否可能使用相机? 懒加载 预创建 使用IdleHandler 在需要时初始化 在合适时机提前初始化 避免主线程阻塞 CameraManager单例 + 权限缓存 减少权限检查次数,避免重复初始化

这个流程的核心就三点:

  • 能不初始化就不初始化——懒加载兜底
  • 要初始化就趁早——预创建打头阵
  • 初始化一次就够了——单例保平安

六、避坑指南

我曾经踩过的坑:

  • 在子线程初始化CameraManager?可以,但后续操作必须切回主线程。因为CameraManager的回调默认在主线程执行。
  • 权限检查结果不要缓存太久。用户可能在设置里动态开关权限,建议每次使用相机前重新确认。
  • 预创建时如果用户没有相机权限,getInstance()不会抛异常,但getCameraIdList()会。记得加try-catch。

说实话,CameraManager初始化优化这件事,看起来简单,但做对的人不多。我见过太多App在启动时一股脑初始化所有服务,结果启动速度慢得离谱。记住一个原则:按需加载,能晚就晚,能省就省

好了,这一章的内容就到这里。CameraManager初始化只是相机启动优化的第一步,后面还有更多有意思的优化点等着我们。


公众号:蓝海资料掘金营,微信deep3321