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,结果权限检查被反复执行。
优化方案其实很简单:
- 缓存实例:全局单例,只初始化一次
- 权限预检查:在初始化前先确认权限状态
- 避免重复获取:不要在循环或频繁回调中调用getInstance()
小技巧:如果你的App在Android 10+上运行,可以试试用CameraManager的getCameraIdList()来触发初始化。这个方法内部会做权限检查,但比getInstance()更轻量。不过要注意,这个方法在Android 9及以下版本可能表现不同。
五、整体优化策略
我把这些优化点整理成一个流程图,方便你理解整体策略:
这个流程的核心就三点:
- 能不初始化就不初始化——懒加载兜底
- 要初始化就趁早——预创建打头阵
- 初始化一次就够了——单例保平安
六、避坑指南
我曾经踩过的坑:
- 在子线程初始化CameraManager?可以,但后续操作必须切回主线程。因为CameraManager的回调默认在主线程执行。
- 权限检查结果不要缓存太久。用户可能在设置里动态开关权限,建议每次使用相机前重新确认。
- 预创建时如果用户没有相机权限,getInstance()不会抛异常,但getCameraIdList()会。记得加try-catch。
说实话,CameraManager初始化优化这件事,看起来简单,但做对的人不多。我见过太多App在启动时一股脑初始化所有服务,结果启动速度慢得离谱。记住一个原则:按需加载,能晚就晚,能省就省。
好了,这一章的内容就到这里。CameraManager初始化只是相机启动优化的第一步,后面还有更多有意思的优化点等着我们。
公众号:蓝海资料掘金营,微信deep3321