CameraX 简单使用

Google AndroidCamera2的基础上再开发了CameraX,用于解决以往Camera/Camera2配置繁杂的问题,力求做到开发者的开箱即用体验,本篇在Android Codelab的基础上,加入了少数注释,力求代码的清晰易懂

Android官方提供的相关指南

准备工作

创建一个新项目

  • 使用 Android Studio 菜单,新建项目并在收到系统提示时选择Empty Activity(空 Activity
    ddc817a03892e44_1920.png
  • 下一步,将应用命名为CameraX App。确保将语言设置为Kotlin、将最低API级别设为21(对于CameraX,这是所需的最低级别),确保AndroidX组件相关设置打开

2383e490b6550aed_1920.png

添加Gradle依赖项

  • 打开 build.gradle(Module: app) 文件并将CameraX依赖项添加到应用Gradle文件中的dependencies部分内:
// CameraX core library using the camera2 implementation
def camerax_version = "1.3.0-beta01"
// The following line is optional, as the core library is included indirectly by camera-camera2
implementation("androidx.camera:camera-core:${camerax_version}")
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}")
// If you want to additionally add CameraX ML Kit Vision Integration
implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
// If you want to additionally use the CameraX Extensions library
implementation("androidx.camera:camera-extensions:${camerax_version}")
  • CameraX需要用到Java 8中的一些方法,因此我们需要对编译选项进行相应设置。 在 android 块末尾,紧跟 buildTypes 的位置添加以下内容:
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
  • 在弹出的消息中选择Sync Now,把在build gradle中配置的更改进行同步
  • 配置成功的build.gradle应该如下所示:
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.mobilescanner.cameraxdemo'
    compileSdk 33

    defaultConfig {
        applicationId "com.mobilescanner.cameraxdemo"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    // CameraX core library using the camera2 implementation
    def camerax_version = "1.3.0-beta01"
    // The following line is optional, as the core library is included indirectly by camera-camera2
    implementation("androidx.camera:camera-core:${camerax_version}")
    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}")
    // If you want to additionally add CameraX ML Kit Vision Integration
    implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
    // If you want to additionally use the CameraX Extensions library
    implementation("androidx.camera:camera-extensions:${camerax_version}")

    implementation 'androidx.core:core-ktx:1.10.1'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'android.arch.lifecycle:livedata:1.1.1'
    implementation 'android.arch.lifecycle:viewmodel:1.1.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

AndroidManifest配置

  • 添加用于确保设备配备有相机的 android.hardware.camera.any。指定 .any,用以表示相机可以是前置摄像头或后置摄像头

如果您使用不带 .anyandroid.hardware.camera,则在您使用没有后置摄像头的设备(例如,大多数 Chromebook)的情况下,此类将无法工作。在第二行中添加对于该相机的访问权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- Declare features -->
    <uses-feature android:name="android.hardware.camera.any" />

    <!-- Declare permissions -->
    <uses-permission android:name="android.permission.CAMERA" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.CameraXDemo"
        tools:targetApi="31">
        <!--下文创建MainActivity时会自动生成 -->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

注意

  • Codelab编写教程时采用了kotlin-android-extensions,但如今其已经被弃用,故文中需要使用控件处都使用findViewById代替,在githubAndroid团队发布的用例中,已经采用了更轻量级的ViewBinding
  • 如果Sync Now过慢,考虑把源更改为阿里源,在目前该文章编写版本的Android Studio(2022.2.1)中,仓库配置源的设置已更改至settings.gradle,配置成功文件如下所示:
pluginManagement {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        maven { url 'https://maven.aliyun.com/repository/google' }
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {

    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        maven { url 'https://maven.aliyun.com/repository/google' }
        google()
        mavenCentral()
    }
}
rootProject.name = "CameraXDemo"
include ':app'

项目实现

创建取景器布局

  • 创建一个MainActivity,并在layout/activity_main.xml布局文件中,填入以下代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/camera_capture_button"
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:layout_marginBottom="50dp"
       android:scaleType="fitCenter"
       android:text="Take Photo"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintBottom_toBottomOf="parent"
       android:elevation="2dp" />

   <androidx.camera.view.PreviewView
       android:id="@+id/viewFinder"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity的基本实现

  • 定义所需常量
package com.example.cameraxdemo
import android.Manifest
import android.content.pm.PackageManager
import android.icu.text.SimpleDateFormat
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.File
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {
    /**
     * TAG:后续编写中需要使用Log.e的Tag
     * FILENAME_FORMAT:该示例中保存图片文件的文件名为时间戳,该变量为时间戳定义形式
     * REQUEST_CODE_PERMISSIONS:申请相机使用权限时的申请码
     * REQUIRED_PERMISSIONS:需要申请使用的权限
     */
    companion object {
        private const val TAG = "CameraXBasic"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
        /**
         * 注意在此处Manifest.permission.CAMERA可能会出现找不到的情况
         * 查看你使用的是否为Android包的Manifest而不是你本项目里的Manifest(删了在Android Studio的提示下重打)
         */
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
  • 申请权限
...
class MainActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, 
                REQUIRED_PERMISSIONS,
                REQUEST_CODE_PERMISSIONS
            )
        }
    }
    private fun startCamera(){ }
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
    override fun onRequestPermissionsResult(
       requestCode: Int, permissions: Array<String>, grantResults:IntArray) {
       //检查请求代码是否正确;如果此代码不正确,则将其忽略。
       if (requestCode == REQUEST_CODE_PERMISSIONS) {
           //如果权限已经被授予,调用相机
           if (allPermissionsGranted()) {
               startCamera()
           } else {
           //权限未授予,告诉用户权限授权不成功
               Toast.makeText(
                   this,
                   "Permissions not granted by the user.",
                   Toast.LENGTH_SHORT).show()
               finish()
           }
       }
    }
}
  • 初始化图片输出文件夹
...
class MainActivity : AppCompatActivity() {
    ...
    private lateinit var outputDirectory: File
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ...
        outputDirectory = getOutputDirectory()
    }
    private fun getOutputDirectory(): File {
        val mediaDir = externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else filesDir
    }
}
  • 初始化线程池
...
class MainActivity : AppCompatActivity() {
    ...
    private lateinit var cameraExecutor: ExecutorService
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ...
         val camera_capture_button = findViewById<Button>(R.id.camera_capture_button)
        // Set up the listener for take photo button
        camera_capture_button.setOnClickListener { takePhoto() }
        cameraExecutor = Executors.newSingleThreadExecutor()
    }
    private fun takePhoto(){ }
    //生命周期结束时关闭线程池,以免内存泄漏
    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
}
  • 运行代码,界面应如下所示

86b7a9ccc8e13fb0_1920.png

实现预览功能

在相机应用中,用户可借助取景器预览他们要拍摄的照片。您可以使用 CameraX Preview 类实现取景器功能。如要使用 Preview,您首先需要定义配置,然后使用该配置创建用例的实例。所生成的实例是您要绑定到CameraX生命周期的内容。

private fun startCamera() {
   val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

   cameraProviderFuture.addListener(Runnable {
       //创建ProcessCameraProvider的实例。
       //此实例用于将相机的生命周期绑定到lifecycler Owner。
       //由于 CameraX 具有生命周期感知能力,所以这样可以省去打开和关闭相机的任务。
       val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

       //Preview预览类
       val preview = Preview.Builder()
          .build()
          .also {
              it.setSurfaceProvider(viewFinder.surfaceProvider())
          }

       //把后置摄像头选为默认相机
       val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
       try {
           //在重新绑定前解绑lifecycle,确保没有任何owner在此之前已经绑定
           cameraProvider.unbindAll()


           //把camera和lifecycle进行绑定
           cameraProvider.bindToLifecycle(
               this, cameraSelector, preview)

       } catch(exc: Exception) {
           Log.e(TAG, "Use case binding failed", exc)
       }
   }, ContextCompat.getMainExecutor(this))
}
  • 运行,此时的预览效果
    392602bf3da0a336_1920.png

拍摄照片并保存

  • takePhoto()的实现
//此处变量声明可在onCreate()前那些变量声明处一起编写
private var imageCapture: ImageCapture? = null
private fun takePhoto() {
   //获取对ImageCapture用例的引用
   //如果用例为 null,则退出函数。
   //如果您在设置拍摄图像之前点按拍照按钮,则这将为 null。
   //如果没有 `return` 语句,则在用例为 `null` 的情况下,应用会崩溃。
   val imageCapture = imageCapture ?: return

   //创建用于保存图片的时间戳File
   val photoFile = File(
       outputDirectory,
       SimpleDateFormat(FILENAME_FORMAT, Locale.US
       ).format(System.currentTimeMillis()) + ".jpg")

   // 创建输出图片设置,包含File和META-DATA
   val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

   //设置iamgeCapture的监听,在图片被拍摄完成后触发
   imageCapture.takePicture(
       outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
           override fun onError(exc: ImageCaptureException) {
               Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
           }

           override fun onImageSaved(output: ImageCapture.OutputFileResults) {
               val savedUri = Uri.fromFile(photoFile)
               val msg = "Photo capture succeeded: $savedUri"
               Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
               Log.d(TAG, msg)
           }
       })
}
  • startCamera()中保存图片设置
private fun startCamera() {
   val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

   cameraProviderFuture.addListener(Runnable {
      ...
       //初始化imageCaptrue
       imageCapture = ImageCapture.Builder().build()
       //把后置摄像头选为默认相机
       val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
       try {
           //在重新绑定前解绑lifecycle,确保没有任何owner在此之前已经绑定
           cameraProvider.unbindAll()
           //把camera和lifecycle进行绑定
           //把camera和imageCaptrue
           cameraProvider.bindToLifecycle(
               this, cameraSelector, preview,imageCapture
               )

       } catch(exc: Exception) {
           Log.e(TAG, "Use case binding failed", exc)
       }
   }, ContextCompat.getMainExecutor(this))
}
  • 运行应用,即可正常拍摄照片并保存

完整代码

package com.mobilescanner.cameraxdemo

import android.Manifest
import android.content.pm.PackageManager
import android.icu.text.SimpleDateFormat
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.File
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

typealias LumaListener = (luma: Double) -> Unit
class MainActivity : AppCompatActivity() {
    /**
     * TAG:后续编写中需要使用Log.e的Tag
     * FILENAME_FORMAT:该示例中保存图片文件的文件名为时间戳,该变量为时间戳定义形式
     * REQUEST_CODE_PERMISSIONS:申请相机使用权限时的申请码
     * REQUIRED_PERMISSIONS:需要申请使用的权限
     */
    companion object {
        private const val TAG = "CameraXBasic"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
        /**
         * 注意在此处Manifest.permission.CAMERA可能会出现找不到的情况
         * 查看你使用的是否为Android包的Manifest而不是你本项目里的Manifest(删了在Android Studio的提示下重打)
         */
    }
    private lateinit var outputDirectory: File
    private lateinit var cameraExecutor: ExecutorService
    private var imageCapture: ImageCapture? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
        val camera_capture_button = findViewById<Button>(R.id.camera_capture_button)
        // Set up the listener for take photo button
        camera_capture_button.setOnClickListener { takePhoto() }

        outputDirectory = getOutputDirectory()

        cameraExecutor = Executors.newSingleThreadExecutor()
    }

    private fun getOutputDirectory(): File {
        val mediaDir = externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else filesDir
    }

    private fun takePhoto() {
        // Get a stable reference of the modifiable image capture use case
        val imageCapture = imageCapture ?: return

        // Create time-stamped output file to hold the image
        val photoFile = File(
            outputDirectory,
            SimpleDateFormat(FILENAME_FORMAT, Locale.US
            ).format(System.currentTimeMillis()) + ".jpg")

        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

        // Set up image capture listener, which is triggered after photo has
        // been taken
        imageCapture.takePicture(
            outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
                    val msg = "Photo capture succeeded: $savedUri"
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.d(TAG, msg)
                }
            })
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        val viewFinder = findViewById<PreviewView>(R.id.viewFinder)
        cameraProviderFuture.addListener(Runnable {
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview
            val preview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(viewFinder.surfaceProvider)
                }
            imageCapture = ImageCapture.Builder()
                .build()
            // Select back camera as a default
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                // Unbind use cases before rebinding
                cameraProvider.unbindAll()

                // Bind use cases to camera
                cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview,imageCapture)

            } catch(exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }

        }, ContextCompat.getMainExecutor(this))
    }
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(this,
                    "Permissions not granted by the user.",
                    Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Service 中使用 CameraX 可以实现在后台拍摄照片或录制视频等功能。下面是一个简单的示例,演示如何在 Service 中使用 CameraX 拍摄照片: 1. 添加 CameraX 依赖库 在项目的 build.gradle 文件中添加 CameraX 的依赖库: ``` dependencies { def camerax_version = "1.0.0-beta01" implementation "androidx.camera:camera-core:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version" } ``` 2. 创建一个 CameraX 实例 在你的 Service 中创建一个 CameraX 的实例,你需要定义一个 Preview 和一个 ImageCapture 对象: ``` private lateinit var cameraProvider: ProcessCameraProvider private lateinit var preview: Preview private lateinit var imageCapture: ImageCapture private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ cameraProvider = cameraProviderFuture.get() // 创建 Preview 对象 preview = Preview.Builder().build() // 创建 ImageCapture 对象 imageCapture = ImageCapture.Builder().build() // 绑定 Preview 和 ImageCapture 对象 val cameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build() val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) // 将 Preview 对象添加到预览视图中 val previewView = findViewById<PreviewView>(R.id.preview_view) preview.setSurfaceProvider(previewView.surfaceProvider) }, ContextCompat.getMainExecutor(this)) } ``` 3. 拍摄照片 在 Service 中使用 imageCapture.takePicture() 方法拍摄照片,你可以将照片保存到本地文件或将其发送到服务器: ``` private fun takePicture() { val file = File(getExternalFilesDir(null), "${System.currentTimeMillis()}.jpg") val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build() imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // 照片保存成功 } override fun onError(exception: ImageCaptureException) { // 照片保存失败 } }) } ``` 4. 开始拍摄 在 Service 的 onStartCommand() 方法中调用 startCamera() 方法来启动相机,并在需要拍摄照片时调用 takePicture() 方法: ``` override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { startCamera() // 开始拍摄照片 takePicture() return START_STICKY } ``` 需要注意的是,在 Service 中使用 CameraX 拍摄照片可能会消耗大量的内存和电池寿命,所以你应该尽可能优化代码,并在拍摄完成后及时释放资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值