一个高性能、功能丰富、可自定义的 Android 相机库 iCamera 的设计和开发过程

本文介绍了作者创建高性能、可自定义的 Android 相机库 iCamera 的过程,包括单例模式用于缓存和预加载、策略模式满足用户自定义需求,以及枚举和耗时分析在优化中的应用。iCamera 解决了现有相机库的性能和功能问题,提供了更好的相机体验。

1、背景介绍

去年年初的时候写过一篇文章 《CameraX:Android 相机库开发实践》,那时我想自己写一个 Android 相机库,但是因为名字和谷歌关放的 CameraX 冲突了,所以现在我将自己的项目改名为 iCamera.

之前的文章中也交代过一些 Android 相机库的背景,本身集成相机功能到自己的项目中并不复杂,但是如果设计一个功能全面的 Android 相机库就没那么简单了——你要满足更多用户的需求,基本的缩放、闪光灯等这些在日常开发中不会涉及的功能都要支持;此外,你还要处理相机的各种支持尺寸和宽高比的计算问题,满足用户自定义的需求等等。

在 iCamera 之前,为了集成相机功能,我也找过一些开源的相机库。比如 CameraView,虽然是挂名谷歌,但是并不算谷歌官方的库,而且因为代码设计的问题,本身性能并不好。再者 CameraFragment,虽然代码结构清晰得多,但是对很多功能的支持不够完善。还有 CameraX,这个库我没有仔细研究它的代码,但是它只支持 Camera2. Camera2 虽然 API 21 上就可以使用,但实际上很多手机对 Camera2 的支持并不好,就比如在我的手机(OnePlus6, API 29)上 Camera1 的启动速率明显高于 Camera2.

综上,我决定自己写一个性能更好、功能全面并且支持用户自定义的相机库。这个项目去年就开始写了,但是因为工作的问题一直没时间完善。最近有了些自己的时间,于是我解决了之前遗留下来的各种问题并做了系统的测试。现在,第一个版本已经正式发布可用了。

项目地址:iCamera

在这篇文章中,我重点介绍下是如何设计和实现这样一款 Android 相机库的,希望这能够对你的系统设计有所启发,并且希望这能够增进你对 iCamera 的了解。

2、整体的设计与实现

下面是这个项目整体的设计图,相比于第一个版本的设计,在下面的这个版本中我又新增了一些方法并对部分设计细节做了调整:

项目整体设计

链接地址:https://www.processon.com/view/link/5c976af8e4b0d1a5b10a4049

了解了整体的设计,我再来具体介绍下我是如何通过多种设计模式的综合运用来满足用户的自定义等需求的。

2.1 单例的应用:缓存、自定义和预加载

最初我在设计的时候希望通过静态字段缓存一些相机的信息,这样一来可以避免多次从相机属性中读取和计算各种参数,二来可以通过预加载操作来把读取相机参数的操作提前到相机启动之前来达到加快相机启动速度的目的。不过后来我发现使用静态单例一样可以满足这个需求并且应该说更加合理一些。于是,我在项目中使用了单例的 ConfigurationProvider 来缓存计算结果并且提供方法提前读取相机信息。该类如下:

public final class ConfigurationProvider {
   
   
    /** The singleton */
    private static volatile ConfigurationProvider configurationProvider;

    /**
     * The sizes map from a int value, which was calculated from:
     * hash = {@link CameraFace} | {@link CameraSizeFor} | {@link CameraType} */
    private SparseArray<List<Size>> sizeMap;

    /**
     * The room ratios map from a int value, which was calculated from:
     * hash = {@link CameraFace} | {@link CameraType} */
    private SparseArray<List<Float>> ratioMap;

    private ConfigurationProvider() {
   
   
        if (configurationProvider != null) {
   
   
            throw new UnsupportedOperationException("U can't initialize me!");
        }
        initWithDefaultValues();
    }

    private void initWithDefaultValues() {
   
   
        // initialize all kinds of parameters
    }

    public static ConfigurationProvider get() {
   
   
        if (configurationProvider == null) {
   
   
            synchronized (ConfigurationProvider.class) {
   
   
                if (configurationProvider == null) {
   
   
                    configurationProvider = new ConfigurationProvider();
                }
            }
        }
        return configurationProvider;
    }
}

对于 Camera2,因为本身它不需要打开相机就能从系统服务中读取相机信息,所以我们可以方便地实现预加载的需求。按照下面这样,我们只需要在打开相机之前调用 prepareCamera2() 就可以提前将相机的信息读取并缓存起来,这样就可以减少相机启动过程中的时间。我测试了下,这样大概可以减少几十毫秒的时间:

public final class ConfigurationProvider {
   
   

    // ....

    private int numberOfCameras;
    private AtomicBoolean camera2Prepared = new AtomicBoolean();
    private SparseArray<String> cameraIdCamera2 = new SparseArray<>(
<think>好的,用户现在想了解日志(log)的作用以及如何通过日志快速查看机进程,同时还询问调用CameraService::connect()的平台过程。我需要结合之前的回答新的引用内容来详细解答。 首先,用户提到的引用[1]中的代码片段显示了一个函数调用,其中有ALOGD打印日志的语句。这说明在CameraService::connect()的过程中,系统会生成关的日志信息,这可以帮助用户调试追踪问题。因此,我需要强调日志在调试中的作用,特别是如何过滤查看关的日志。 关于日志的作用,我需要分点说明,比如问题诊断、流程跟踪、权限检查等,并举例说明常见的日志标签如CameraService、CameraClient。同时,用户想知道如何查看机进程,可能需要指导他们使用adb logcat命令,结合grep或findstr来过滤日志,或者使用特定tag来缩小范围。 接下来是调用CameraService::connect()的平台流程。之前的回答已经提到过,应用层通过Java API调用,经过JNI到Native层,再通过Binder到CameraService。需要再次强调这个过程,并说明直接通过命令行调用需要特定条件,比如编写C++程序或使用adb命令,但需要root权限或系统签名。 用户可能在实际操作中遇到权限问题,比如SELinux限制,或者版本兼容性问题,需要给出调试建议,比如检查avc: denied日志,或者确认Android版本差异。 还需要检查用户是否有其他潜在需求。例如,用户可能想了解如何编写自定义程序来调用CameraService,或者如何在没有源码的情况下进行调试。不过根据当前问题,重点应放在日志的使用调用流程上。 最后,生成的关问题需要围绕日志分析调用流程展开,比如如何过滤日志,Binder通信机制,SELinux权限配置等,确保问题与之前的内容连贯,帮助用户深入理解。</think>### 日志作用与机进程追踪指南 --- #### 一、日志的核心作用与关标记 1. **日志核心价值** - **问题诊断**:记录硬件初始化失败、权限错误等关键事件(如 `CameraService: connect() failed due to permission denial`)[^1][^3] - **流程跟踪**:捕捉 `CameraService::connect()` 调用链路(如引用[1]中的 `ALOGD("%s:weiqifaconnect234",__FUNCTION__)` 调试信息) - **性能分析**:统计机打开耗时、帧率波动等(通过 `dumpsys media.camera` 获取详细状态) 2. **Android机专用日志标签** | 日志标签(TAG) | 作用范围 | 典型输出示例 | |---------------------|----------------------------------|---------------------------------------| | `CameraService` | 服务端连接/权限控制 | `CameraService::connect X called for camera 0` | | `CameraClient` | 客户端会话管理 | `Client::initialize: cameraId=0` | | `CameraHardware` | HAL层硬件交互 | `startPreview: received frame 120` | --- #### 二、通过日志追踪机进程(实战步骤) 1. **基础过滤命令** ```bash adb logcat -v time | grep -Ei "CameraService|CameraClient|cameraserver" ``` - **Windows** 使用 `findstr` 替代 `grep` - **关键过滤词**:`CAMERA_ERR`、`avc: denied`(SELinux错误)、`Binder transaction`(跨进程通信问题) 2. **深度调试模式** 启用 **Binder调用追踪** 以捕捉 `connect()` 跨进程细节: ```bash adb shell setprop log.tag.CameraService VERBOSE adb logcat -b all | grep Binder:cameraserver ``` 3. **典型问题定位案例** - **权限拒绝**: ``` 07-01 10:23:45.123 W CameraService: connect() rejected (PID 10102): permission denied ``` 解决方案:检查 `AndroidManifest.xml` 的 `<uses-permission android:name="android.permission.CAMERA" />` 声明[^1] - **Binder通信超时**: ``` 07-01 10:25:12.456 E Binder : cameraserver Binder transaction failed: TIMED_OUT ``` 解决方案:检查 `CameraService` 是否被阻塞,或增加 `Binder` 超时阈值 --- #### 三、CameraService::connect() 调用全流程解析 1. **平台与工具依赖** | 调用方式 | 必要条件 | 典型场景 | |------------------|---------------------------------------------|-----------------------------------| | **应用层调用** | 标准 Android SDK + CAMERA权限 | 常规APP开发 | | **Native层调用** | NDK + `libcamera_client.so` + `libbinder.so` | 系统服务开发或深度定制 | | **命令行触发** | Root权限 + 系统签名 | 测试环境调试(需自定义可执行文件)| 2. **跨进程调用流程图解** ``` 应用层 (Java/Kotlin) ↓ Camera.open() JNI桥接层 (android_hardware_Camera.cpp) ↓ CameraBase::connect() Binder IPC (ICameraService.aidl) ↓ CameraService::connect() CameraClient初始化 ↓ HAL层交互 (CameraHardwareInterface) ``` 3. **命令行调用实验方法** - **步骤1**:编写包含以下代码的 Native 程序(需链接系统库) ```cpp sp<ICameraService> cs = getCameraService(); // 通过 ServiceManager 获取 sp<ICamera> cameraDevice; status_t res = cs->connect(new MyCameraClient(), cameraId, String16("test"), 0, &cameraDevice); ``` - **步骤2**:通过 `mm` 命令在 AOSP 环境下编译,推送至 `/system/bin/` - **步骤3**:执行时需关闭 SELinux 或添加策略规则 ```bash adb shell setenforce 0 # 临时关闭SELinux adb shell /system/bin/my_camera_test ``` --- #### 四、高级调试技巧 1. **SELinux策略适配** 在 `device/[manufacturer]/[device]/sepolicy/` 中添加规则: ```te allow my_app cameraserver:service_manager find; allow cameraserver my_app:fd use; ``` 2. **Binder事务分析工具** 使用 `batterystats` 或 `systrace` 监控机服务资源占用: ```bash adb shell dumpsys batterystats --camera ``` --- #### 五、典型问题与解决方案速查表 | 问题现象 | 日志特征 | 解决方案 | |-----------------------------------|-----------------------------------|-----------------------------------| | 机打开黑屏 | `HAL returned invalid buffer` | 检查 HAL 层图像格式配置 | | 机服务崩溃 | `Fatal signal 11 in cameraserver`| 验证 Binder 接口版本兼容性 | | 多应用同时访问失败 | `MAX_CAMERAS_IN_USE reached` | 优化资源释放逻辑或增加系统限制 | --- ### 关问题 1. 如何过滤出 CameraService 与 HAL 层的完整交互日志? 2. Binder 通信超时导致 CameraService 调用失败应如何优化? 3. 在非 Root 设备上如何安全地调试 CameraService 关功能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值