Android CameraX学习记录(一) kotlin 实现预览
CameraX 结构
- Preview[ 预览 ]:接受用于显示预览的图面,例如 .PreviewView
- ImageAnalysis[ 图片分析 ]:为分析(例如机器学习)提供 CPU 可访问的缓冲区。
- ImageCapture[ 图片拍摄 ]:拍摄并保存照片。
- VideoCapture[ 视频拍摄 ]:通过 VideoCapture 拍摄视频和音频
查看源码
Preview
ImageAnalysis
ImageCapture
VideoCapture
这几个类都继承UseCase,
他们都属于用例,如需使用他们需要bindToLifecycle()
函数在生命周期里面进行绑定
CameraX 使用的类
ProcessCameraProvider
一个单例,可用于将相机的生命周期绑定到应用程序进程中的任何生命周期所有者。一个进程中只能存在一个进程相机提供程序,并且可以使用 getInstance(Context)
检索它。重量级资源,例如打开和正在运行的相机设备,将限定为提供给 bindToLifecycle
的生命周期(生命周期所有者、相机选择器、用例…)。其他轻量级资源(如静态相机特征)可以在使用 getInstance(Contex)
首次检索此提供程序时检索和缓存,并将在进程的生存期内持续存在。这是应用程序要使用的标准提供程序。
这是应用程序要使用的标准提供程序。
相机选择器 cameraSelector
用于选择相机或返回一组筛选的相机。
DEFAULT_BACK_CAMERA 选择默认后置摄像头的静态。
DEFAULT_FRONT_CAMERA 选择默认前置摄像头的静态。
LENS_FACING_BACK = 1 设备上的摄像头,其方向与设备屏幕相反。
LENS_FACING_FRONT = 0 设备上的摄像头与设备屏幕朝向同一方向。
CameraX 使用流程
权限
使用相机需要声明权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
<!-- 应用程序至少用到一个摄像头,无所谓其朝向,如果有连接的外置摄像头也可以。 如果不是非要使用后置摄像头的话,可优先选用本设置,而不是 android.hardware.camera。 -->
uses-feature参考:Android开发-API指南-<uses-feature>
如需将图片保存到文件中,除非所用设备搭载 Android 10 或更高版本,否则应用还需要 WRITE_EXTERNAL_STORAGE 权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
// 请求相机权限
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
0x1001)
// 监听权限
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 0x1001){
if (permissions.contains(
Manifest.permission.CAMERA) && grantResults.get(0) == 0){
startCamera()
}
}
}
添加依赖
将以下内容添加到app目录下的 build.gradle 文件中:
dependencies {
// CameraX core library using the camera2 implementation
def camerax_version = "1.3.0-alpha02"
implementation("androidx.camera:camera-camera2:${camerax_version}")
// If you want to additionally use the CameraX Lifecycle library
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
// If you want to additionally use the CameraX VideoCapture library
implementation("androidx.camera:camera-video:${camerax_version}")
// If you want to additionally use the CameraX View class
implementation("androidx.camera:camera-view:${camerax_version}")
}
布局
编写activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
生命周期
CameraX 没有在onResume()
和 onPause()
中放置具体的启动和停止方法调用,而是使用cameraProvider.bindToLifecycle()
指定要与相机关联的生命周期。之后,该生命周期会告知 CameraX 何时配置相机拍摄会话并确保相机状态随生命周期的转换相应地变化。
请求 CameraProvider
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
检查 CameraProvider 可用性
请求 CameraProvider
后,请验证它能否在视图创建后成功初始化。以下代码展示了如何执行此操作:
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
bindPreview(cameraProvider)
}, ContextCompat.getMainExecutor(this))
预览
// 预览
val preview = Preview.Builder().build()
// 使用以下代码将用例绑定到Android生命周期
val camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
// PreviewView创建一个表面提供程序,并且是推荐的提供程序
camera = preview.setSurfaceProvider(previewView.getSurfaceProvider())
bindToLifecycle()
会返回一个 Camera
对象。
bindToLifecycle()
函数
查看bindToLifecycle()函数源码可以得知
public Camera bindToLifecycle(LifecycleOwner lifecycleOwner,
CameraSelector cameraSelector,
UseCase... useCases)
lifecycleOwner
:生命周期所有者,用于控制用例的生命周期转换。
cameraSelector
:相机选择器,用于确定用于一组用例的相机。
useCases
:要绑定到生命周期的用例。
Preview[ 预览 ],Image analysis[ 图片分析 ],Image capture[ 图片拍摄 ],Video capture[ 视频拍摄 ]
查看源码可知他们父类都是
UseCase
,因此要使用哪个用例需要绑定到生命周期
预览完整代码
private fun startCamera(){
// 将相机的生命周期绑定到应用程序进程中 "getInstance"一个进程中只能存在一个进程相机提供程序,可以使用 进行检索。
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
bindPreview(cameraProviderFuture)
}
// 用于将相机的生命周期绑定到生命周期所有者
private fun bindPreview(cameraProviderFuture: ListenableFuture<ProcessCameraProvider>){
cameraProviderFuture.addListener(Runnable {
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// 创建预览 Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
// 选择后置摄像头作为默认值
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 在重新绑定之前取消绑定用例Unbind use cases before rebinding
cameraProvider.unbindAll()
// 将用例绑定到相机 Bind use cases to camera
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
},
// 返回一个将在与此上下文关联的主线程上运行排队任务
ContextCompat.getMainExecutor(this)
)
}
参考资料:
CameraX 入门指南
CameraX 概览