You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Capacitor/Swift/Kotlin同时拍摄前后置摄像头照片(仿BeReal)

解决Capacitor/原生实现同时调用前后置摄像头(单次权限授权)

Capacitor 优化方案

你遇到的重复权限弹窗问题,根源在于两次调用Camera.getPhoto时,每次都会触发系统权限检查。只需提前手动申请一次相机权限,后续调用拍照接口就不会再弹授权窗。

优化后代码

import { Camera, CameraDirection, CameraResultType, CameraSource, CameraPermissions } from '@capacitor/camera';

const getDualPhotos = async () => {
  // 提前检查并请求相机权限,仅触发一次弹窗
  const permissionStatus = await Camera.checkPermissions();
  if (permissionStatus.camera !== 'granted') {
    const requestResult = await Camera.requestPermissions();
    if (requestResult.camera !== 'granted') {
      throw new Error('相机权限未授权,无法拍照');
    }
  }

  // 并行调用前后置拍照,不再触发权限检查
  const [rearPhoto, frontPhoto] = await Promise.all([
    Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      source: CameraSource.Camera,
      resultType: CameraResultType.Base64,
      direction: CameraDirection.Rear
    }),
    Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      source: CameraSource.Camera,
      resultType: CameraResultType.Base64,
      direction: CameraDirection.Front
    })
  ]);

  // 处理两张照片数据
  console.log('后置照片Base64:', rearPhoto.base64String);
  console.log('前置照片Base64:', frontPhoto.base64String);
};

关键说明

  • 相机权限本身已包含访问前后置摄像头的权限,无需分别申请
  • 用Promise.all并行调用拍照接口,比串行调用效率更高

原生实现方案(复刻BeReal同步拍摄逻辑)

如果需要更精准的同步拍摄控制(比如实时双摄像头预览),可以直接用原生代码实现:

Swift(iOS)实现

import UIKit
import AVFoundation

class DualCameraHandler: NSObject, AVCapturePhotoCaptureDelegate {
    private var rearSession: AVCaptureSession!
    private var frontSession: AVCaptureSession!
    private var rearPhotoOutput: AVCapturePhotoOutput!
    private var frontPhotoOutput: AVCapturePhotoOutput!
    
    // 请求相机权限并初始化双摄像头
    func setupDualCameras() {
        AVCaptureDevice.requestAccess(for: .video) { granted in
            guard granted else { return }
            DispatchQueue.main.async {
                self.configureRearCamera()
                self.configureFrontCamera()
            }
        }
    }
    
    // 配置后置摄像头
    private func configureRearCamera() {
        rearSession = AVCaptureSession()
        rearSession.sessionPreset = .photo
        
        guard let rearDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
        do {
            let input = try AVCaptureDeviceInput(device: rearDevice)
            if rearSession.canAddInput(input) {
                rearSession.addInput(input)
            }
            
            rearPhotoOutput = AVCapturePhotoOutput()
            if rearSession.canAddOutput(rearPhotoOutput) {
                rearSession.addOutput(rearPhotoOutput)
            }
            rearSession.startRunning()
        } catch {
            print("后置摄像头配置失败:\(error)")
        }
    }
    
    // 配置前置摄像头
    private func configureFrontCamera() {
        frontSession = AVCaptureSession()
        frontSession.sessionPreset = .photo
        
        guard let frontDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else { return }
        do {
            let input = try AVCaptureDeviceInput(device: frontDevice)
            if frontSession.canAddInput(input) {
                frontSession.addInput(input)
            }
            
            frontPhotoOutput = AVCapturePhotoOutput()
            if frontSession.canAddOutput(frontPhotoOutput) {
                frontSession.addOutput(frontPhotoOutput)
            }
            frontSession.startRunning()
        } catch {
            print("前置摄像头配置失败:\(error)")
        }
    }
    
    // 同步拍摄前后置照片
    func captureDualPhotos() {
        let rearSettings = AVCapturePhotoSettings()
        let frontSettings = AVCapturePhotoSettings()
        
        rearPhotoOutput.capturePhoto(with: rearSettings, delegate: self)
        frontPhotoOutput.capturePhoto(with: frontSettings, delegate: self)
    }
    
    // 处理拍摄结果
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if let error = error {
            print("拍照失败:\(error)")
            return
        }
        
        let isRear = output == rearPhotoOutput
        if let imageData = photo.fileDataRepresentation() {
            // 可将图片数据转为UIImage或保存到本地
            if isRear {
                print("后置照片已获取")
            } else {
                print("前置照片已获取")
            }
        }
    }
}

Kotlin(Android)实现

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class DualCameraActivity : AppCompatActivity() {
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private lateinit var cameraExecutor: ExecutorService
    private var rearImageCapture: ImageCapture? = null
    private var frontImageCapture: ImageCapture? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        cameraExecutor = Executors.newSingleThreadExecutor()
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        
        // 检查并请求相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1001)
        } else {
            setupDualCameras()
        }
    }
    
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 1001 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            setupDualCameras()
        }
    }
    
    // 配置双摄像头
    private fun setupDualCameras() {
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            
            // 绑定后置摄像头
            val rearCapture = ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()
            rearImageCapture = rearCapture
            
            try {
                cameraProvider.bindToLifecycle(
                    this, CameraSelector.DEFAULT_BACK_CAMERA, rearCapture
                )
            } catch (e: Exception) {
                print("后置摄像头绑定失败:${e.message}")
            }
            
            // 绑定前置摄像头
            val frontCapture = ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()
            frontImageCapture = frontCapture
            
            try {
                cameraProvider.bindToLifecycle(
                    this, CameraSelector.DEFAULT_FRONT_CAMERA, frontCapture
                )
            } catch (e: Exception) {
                print("前置摄像头绑定失败:${e.message}")
            }
        }, ContextCompat.getMainExecutor(this))
    }
    
    // 同步拍摄两张照片
    fun captureDualPhotos() {
        rearImageCapture?.let { capture ->
            val outputOptions = ImageCapture.OutputFileOptions.Builder(createTempFile()).build()
            capture.takePicture(
                outputOptions, cameraExecutor,
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        print("后置照片已保存:${outputFileResults.savedUri}")
                    }

                    override fun onError(exc: ImageCaptureException) {
                        print("后置拍照失败:${exc.message}")
                    }
                }
            )
        }
        
        frontImageCapture?.let { capture ->
            val outputOptions = ImageCapture.OutputFileOptions.Builder(createTempFile()).build()
            capture.takePicture(
                outputOptions, cameraExecutor,
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        print("前置照片已保存:${outputFileResults.savedUri}")
                    }

                    override fun onError(exc: ImageCaptureException) {
                        print("前置拍照失败:${exc.message}")
                    }
                }
            )
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
}

核心逻辑总结

  1. 系统相机权限是统一授权,无需为前后置摄像头分别申请
  2. Capacitor插件的getPhoto方法默认每次调用都会触发权限检查,提前手动申请即可避免重复弹窗
  3. 原生实现可直接控制双摄像头会话,实现真正的同步拍摄,适合对延迟要求高的场景

内容的提问来源于stack exchange,提问作者user82395214

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 06:55:15