CameraX实战:解锁Android外接USB摄像头开发新姿势
1. 为什么需要CameraX外接USB摄像头支持
在Android设备上使用外接USB摄像头一直是个头疼的问题。传统Camera API和Camera2 API虽然功能强大,但代码复杂度高,尤其是面对非手机设备(如工业平板、收银机、智能终端)时,外接摄像头的适配往往需要厂商定制驱动。我去年给某连锁超市改造自助收银机时就深有体会——他们的扫码摄像头都是USB接口的,用传统方式开发光是摄像头枚举就写了200多行代码。
CameraX 1.3.0-alpha版本带来的LENS_FACING_EXTERNAL配置项简直是救星。这个设计初衷很明确:为日益增长的Android非手机设备(特别是商用工控设备)提供标准化的外接摄像头支持。实测在搭载Android 10以上的设备,接入常见的Logitech C920、A4Tech PK-635G等USB摄像头都能即插即用。
与传统方案相比,CameraX的优势主要体现在三个层面:
- 代码量减少70%:不再需要手动处理CameraDevice.StateCallback等复杂回调
- 生命周期自动管理:不再出现因Activity销毁导致的摄像头资源泄漏
- 统一API体验:无论内置还是外接摄像头,都用同一套CameraSelector控制
2. 环境配置与权限处理
2.1 基础环境搭建
先说说我踩过的坑:最开始在Android 9设备上测试时,发现USB摄像头始终无法识别。后来发现是Gradle配置漏了关键项。完整的环境配置应该包含以下步骤:
- 在
settings.gradle中添加JitPack仓库(某些USB摄像头需要额外驱动支持):
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
- 模块级
build.gradle的依赖配置(注意要用alpha版本):
dependencies {
def camerax_version = "1.3.0-alpha04"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:1.3.0-alpha04"
}
提示:如果遇到"Manifest merger failed"错误,在AndroidManifest.xml中添加
<uses-feature android:name="android.hardware.camera.any" android:required="false"/>
2.2 权限处理最佳实践
很多教程忽略了一个关键点:USB摄像头可能需要额外权限。除了常规的CAMERA权限,还需要在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 部分摄像头带麦克风 -->
<uses-feature android:name="android.hardware.usb.host" /> <!-- USB主机模式必须 -->
动态权限请求建议用以下代码(兼容Android 6.0+):
private val requiredPermissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
fun checkPermissions() {
val missingPermissions = requiredPermissions.filter {
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
}
if (missingPermissions.isNotEmpty()) {
requestPermissions(missingPermissions.toTypedArray(), REQUEST_CODE_PERMISSIONS)
} else {
startCamera()
}
}
3. 外接摄像头核心实现
3.1 CameraSelector配置技巧
CameraX 1.3.0-alpha最大的亮点就是支持LENS_FACING_EXTERNAL选择器。但实际使用时要注意几个细节:
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_EXTERNAL)
.build()
这个简单的配置背后有几个隐藏知识点:
- 多摄像头处理:当连接多个USB摄像头时,可以通过
CameraProvider.getAvailableCameraInfos()获取设备列表,然后根据CameraInfo.getLensFacing()筛选外部设备 - 方向校正:外接摄像头的默认旋转角度可能不正确,需要通过
Preview.Builder.setTargetRotation()手动校正 - 分辨率适配:建议使用
QualitySelector指定分辨率,避免某些USB摄像头返回不支持的格式
3.2 完整的预览绑定流程
这是我优化过的绑定代码,增加了异常处理和设备检测:
private fun bindCameraUseCases() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 检查是否有外接摄像头
val hasExternalCamera = cameraProvider.availableCameraInfos.any {
(it as CameraInfo).lensFacing == CameraSelector.LENS_FACING_EXTERNAL
}
if (!hasExternalCamera) {
showToast("未检测到外接摄像头")
return@addListener
}
val preview = Preview.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
.build()
.also { it.setSurfaceProvider(previewView.surfaceProvider) }
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this,
CameraSelector.DEFAULT_EXTERNAL_CAMERA,
preview
)
} catch (e: Exception) {
Log.e(TAG, "绑定失败: ${e.message}")
}
}, ContextCompat.getMainExecutor(this))
}
4. 常见问题与兼容性处理
4.1 设备兼容性问题排查
在测试了20+款Android设备后,我整理出这份兼容性对照表:
| 设备类型 | 系统版本要求 | 常见问题 | 解决方案 |
|---|---|---|---|
| 商显平板 | Android 10+ | 供电不足导致掉线 | 使用带外接电源的USB Hub |
| 工业手持终端 | Android 9+ | 图像旋转90度 | 设置targetRotation为Surface.ROTATION_90 |
| 自助收银机 | Android 11+ | 无法识别UVC摄像头 | 检查内核是否启用CONFIG_USB_VIDEO_CLASS |
4.2 性能优化建议
- 帧率控制:某些USB摄像头在默认设置下帧率不稳定,可以通过以下方式锁定:
Preview.Builder()
.setSupportedResolutions(listOf(Size(1280, 720)))
.setDefaultResolution(Size(1280, 720))
- 内存优化:长时间预览时建议启用纹理缓存:
<androidx.camera.view.PreviewView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:implementationMode="COMPATIBLE" /> <!-- 使用TextureView -->
- 热插拔处理:注册USB设备监听广播:
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == UsbManager.ACTION_USB_DEVICE_ATTACHED) {
restartCamera()
}
}
}
override fun onResume() {
super.onResume()
registerReceiver(usbReceiver, IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED))
}
5. 扩展功能实现
5.1 多摄像头协同工作
在智能零售场景中,经常需要同时使用内置和外接摄像头。这是经过验证的多摄像头方案:
fun setupDualCamera() {
val backCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
val externalCameraSelector = CameraSelector.DEFAULT_EXTERNAL_CAMERA
val backPreview = Preview.Builder().build().apply {
setSurfaceProvider(backPreviewView.surfaceProvider)
}
val externalPreview = Preview.Builder().build().apply {
setSurfaceProvider(externalPreviewView.surfaceProvider)
}
cameraProvider.bindToLifecycle(
this,
backCameraSelector,
backPreview
)
cameraProvider.bindToLifecycle(
this,
externalCameraSelector,
externalPreview
)
}
5.2 高级图像处理
结合CameraX的ImageAnalysis可以实现实时处理:
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor) { imageProxy ->
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
// 在这里添加OpenCV处理逻辑
imageProxy.close()
}
}
最后提醒几个容易忽略的细节:
- 外接摄像头的对焦模式可能需要手动设置为INFINITY
- 某些工业摄像头需要先发送USB控制指令才能启动
- 在Android 12+设备上需要单独申请BLUETOOTH_CONNECT权限才能使用部分无线摄像头
更多推荐



所有评论(0)