前言:

最近为了实现拍照方面的工作内容,思前想后决定使用Camera X作为这个工程的拍照API,原因主要有:1、API使用方面没有Camera V2 API所需的代码量大,虽然已经有过相关的工作经验,但想起其代码量,对比了一下Camera X的例子,还是不太想用。2、兼容性更好,而且提供的常用API基本满足要求,像对焦、闪光灯等常用API都有了,更适合作为Demo快速建立。

问题:

在我根据Android developer中所介绍的例子进行了自己的demo搭建后,我对Camera X的适用已经有了一定的了解。但是发现OnPause之后再回来,预览画面就丢失了。于是我想到了两个可能:

1、PreviewView可能被销毁引起的问题。

2、和相机的生命周期有关,我可能没有使用好。

首先从第一个可能入手。试着在OnResume的时候调用如下代码:

        if (mPreview != null) {//为预览窗口添加surface通道mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());}

确实OnResume之后预览画面能重新出来,但却依然要等5秒钟。于是怀疑更可能和可能性2有关。

按照之前Camera V2的经验,如果相机资源没释放导致一些异常,那么一般其他要调用相机的应用也会出问题。于是我先打开自己的demo,再打开微信扫一扫,发现扫一扫的预览也黑屏了5秒才出来,比较符合上述直觉和经验。于是在OnPause中调用unBindAll释放相机,OnResume的时候再重新初始化并设定预览窗,问题果然就解决了。

详细代码:

相机逻辑:

package com.example.cameraXDemo;import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageView;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
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 androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;import com.bumptech.glide.Glide;
import com.example.piccut.R;
import com.google.common.util.concurrent.ListenableFuture;import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CameraXDemoActivity_1 extends Activity implements LifecycleOwner {private ProcessCameraProvider mCameraPRrovider = null;private int mLensFacing = CameraSelector.LENS_FACING_BACK;private PreviewView mPreviewView;private LifecycleRegistry mLifecycleRegistry;/**拍照器**/private ImageCapture mImageCapture;private ExecutorService mTakePhotoExecutor;private Button mBtnTakePhoto;private ImageView mImagePhoto;private Preview mPreview;private Button mBtnFlashLight;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//拍照专用线程,让它不要卡住主线程:mTakePhotoExecutor = Executors.newSingleThreadExecutor();//权限申请if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);}mLifecycleRegistry = new LifecycleRegistry(this);mLifecycleRegistry.markState(Lifecycle.State.CREATED);setContentView(R.layout.camera_x_demo);mBtnFlashLight = findViewById(R.id.btn_flash_light);mPreviewView = (PreviewView) findViewById(R.id.pv);mBtnTakePhoto = findViewById(R.id.btn_take_photo);mImagePhoto = findViewById(R.id.iv_photo);//拍照按钮mBtnTakePhoto.setOnClickListener(v -> {takePhoto(mImageCapture);});//闪光灯mBtnFlashLight.setOnClickListener(v -> {switch ((String) v.getTag()) {case "auto":mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_ON);((Button) v).setText("闪光灯:常开");v.setTag("on");break;case "on":mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_OFF);((Button) v).setText("闪光灯:关闭");v.setTag("off");break;default:mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);((Button) v).setText("闪光灯:自动");v.setTag("auto");break;}});}@Overridepublic void onConfigurationChanged(@NonNull Configuration newConfig) {super.onConfigurationChanged(newConfig);bindCameraUseCases(mCameraPRrovider, null);}@Overrideprotected void onStart() {super.onStart();mLifecycleRegistry.markState(Lifecycle.State.STARTED);}@Overrideprotected void onResume() {super.onResume();ListenableFuture<ProcessCameraProvider> processCameraProvider = ProcessCameraProvider.getInstance(this);//回来的时候要重新绑定一下:processCameraProvider.addListener(() -> {try {mCameraPRrovider = processCameraProvider.get();//绑定预览窗等bindCameraUseCases(mCameraPRrovider, null);} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}, getMainExecutor()); //只能在主线程mLifecycleRegistry.markState(Lifecycle.State.RESUMED);}@Overrideprotected void onPause() {super.onPause();//出去的时候要释放相机资源mCameraPRrovider.unbindAll();}@Overrideprotected void onDestroy() {super.onDestroy();mLifecycleRegistry.markState(Lifecycle.State.DESTROYED);mTakePhotoExecutor.shutdown();}/** Returns true if the device has an available back camera. False otherwise */private boolean hasBackCamera() {try {return mCameraPRrovider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA);} catch (CameraInfoUnavailableException e) {e.printStackTrace();}return false;}/** Returns true if the device has an available front camera. False otherwise */private boolean hasFrontCamera() {try {return mCameraPRrovider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA);} catch (CameraInfoUnavailableException e) {e.printStackTrace();}return false;}private void bindCameraUseCases(ProcessCameraProvider cameraProvider, Surface surface) {int aspectRatio = AspectRatio.RATIO_4_3;//预览界面:mPreview = new Preview.Builder().setTargetAspectRatio(aspectRatio).setTargetRotation(mPreviewView.getDisplay().getRotation()).build();//为预览窗口添加surface通道mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());//照片抓取:mImageCapture = new ImageCapture.Builder().setTargetAspectRatio(aspectRatio)//低延迟拍照,反应速度会比较快.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY).setTargetRotation(mPreviewView.getDisplay().getRotation()).setFlashMode(ImageCapture.FLASH_MODE_AUTO) //闪光灯调节.build();ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetAspectRatio(aspectRatio).setTargetRotation(mPreviewView.getDisplay().getRotation()).build();//解绑之前可能存在的绑定关系cameraProvider.unbindAll();//有后置摄像头就选后置,否则用前置:if (hasBackCamera()) {mLensFacing = CameraSelector.LENS_FACING_BACK;} if (hasFrontCamera()) {
//            mLensFacing = CameraSelector.LENS_FACING_FRONT;} else {throw new IllegalStateException("前后摄像头都没");}CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(mLensFacing).build();//绑定生命周期、预览窗、拍照获取器等cameraProvider.bindToLifecycle(this,cameraSelector, mPreview, mImageCapture, imageAnalysis);}/**实际拍照逻辑**/private void takePhoto(ImageCapture imageCapture) {if (null == imageCapture) {Log.e("cjztest", "imageCapture is null");return;}String fileFormatPattern = "yyyy-MM-dd-HH-mm-ss-SSS";SimpleDateFormat simpleDateFormat = new SimpleDateFormat(fileFormatPattern);//先存放到APP本地文件夹:
//        File cacheFileDir = getCacheDir(); //APP内cache地址File cacheFileDir = getExternalCacheDir(); //共用的、大家都可以访问的cache地址if (cacheFileDir.exists() && cacheFileDir.isDirectory()) {File newFile = new File(cacheFileDir.getAbsolutePath() + String.format("/%s.jpg", simpleDateFormat.format(System.currentTimeMillis())));Log.i("cjztest", "newFile:" + newFile.getAbsolutePath());try {newFile.createNewFile();if (!newFile.exists()) {return;}ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(newFile).setMetadata(new ImageCapture.Metadata()).build();//照片拍好之后从回调里面接收imageCapture.takePicture(outputOptions, mTakePhotoExecutor, new ImageCapture.OnImageSavedCallback() {@Overridepublic void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {Uri savedUri = outputFileResults.getSavedUri() == null ?Uri.fromFile(newFile) :outputFileResults.getSavedUri();//给按钮弄个照片runOnUiThread(() -> {if (newFile.exists()) {Glide.with(mImagePhoto).load(newFile)
//                                .apply(RequestOptions.circleCropTransform()).into(mImagePhoto);}});// We can only change the foreground Drawable using API level 23+ APIif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// Update the gallery thumbnail with latest picture taken
//                            setGalleryThumbnail(savedUri);}// Implicit broadcasts will be ignored for devices running API level >= 24// so if you only target API level 24+ you can remove this statementif (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri));}// If the folder selected is an external media directory, this is// unnecessary but otherwise other apps will not be able to access our// images unless we scan them using [MediaScannerConnection]String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(".jpg");MediaScannerConnection.scanFile(CameraXDemoActivity_1.this,new String[] {savedUri.getPath()},new String[] {mimeType},new MediaScannerConnection.OnScanCompletedListener() {@Overridepublic void onScanCompleted(String path, Uri uri) {Log.i("cjztest", "新文件扫描完成, 文件大小:" + newFile.length());}});}@Overridepublic void onError(@NonNull ImageCaptureException exception) {Log.e("cjztest", "拍照失败");}});} catch (IOException e) {Log.e("cjztest", "创建文件失败");e.printStackTrace();}}Log.i("cjztest", "cacheFileDir:" + cacheFileDir);}@NonNull@Overridepublic Lifecycle getLifecycle() {return mLifecycleRegistry;}
}

界面描述:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"android:background="#FF000000"><Buttonandroid:id="@+id/btn_flash_light"android:text="闪光灯:自动"android:tag="auto"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right|top"/><androidx.camera.view.PreviewViewandroid:id="@+id/pv"android:layout_width="match_parent"android:layout_height="700dp"/><ImageViewandroid:id="@+id/iv_photo"android:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|left"android:scaleType="fitXY"/><Buttonandroid:id="@+id/btn_take_photo"android:text="拍照"android:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|center"/></FrameLayout>

Gitee地址:

lvlv/AndroidDemo大全 - Gitee.com

使用Camera X遇到的坑_OnPause时没有释放相机导致回来时黑屏相关推荐

  1. 虚拟机登录时输入密码正确,进入界面后黑屏闪退回登陆界面,死循环问题,已解决

    哈喽!我是泡泡! 问题描述 最近安装gp数据库的过程中,发现虚拟机登录会闪退回登陆界面,反复循环,我用的是CentOS7. 搜了好多都说是环境变量的问题,说让修改/etc/profile文件,我修改了 ...

  2. dnf正在连接服务器然后选择角色卡,地下城与勇士DNF比赛服登陆时卡在“正在连接服务器”的黑屏界面中的解决方法...

    DNF进不去游戏了,如下图: 一直卡在 正在连接服务器 黑屏状态,经过大神指点: 首先显示隐藏文件和系统文件: 我们先找到存DNF.cfg这个文件的文件夹. 路径:C盘-Users(用户)-自己的用户 ...

  3. 卡在正在连接服务器,地下城与勇士DNF比赛服登陆时卡在“正在连接服务器”的黑屏界面中的解决方法...

    DNF进不去游戏了,如下图: 一直卡在 正在连接服务器 黑屏状态,经过大神指点: 首先显示隐藏文件和系统文件: 我们先找到存DNF.cfg这个文件的文件夹. 路径:C盘-Users(用户)-自己的用户 ...

  4. 微软修复Win7 SP1安装时出现的黑屏错误“0xC0000009A”

    如果你是Windows 7或Windows Server 2008 R2用户,在安装SP1服务包时可能会遇到"0xC0000009A"错误提示,微软不久前发布了KB2534366, ...

  5. Ubuntu18.04安装显卡驱动导致开机时卡在『ok』‘starting Gnome display manager

    #Ubuntu18.04安装显卡驱动导致开机时卡在『ok』'starting Gnome display manager' 新手小白第一次安装显卡驱动,reboot之后就一直进不去,冷静下来开始寻找c ...

  6. 计算机长时间不黑屏怎么设置,当计算机长时间不移动鼠标时,如何设置黑屏?如何将计算机设置为长时间不出现黑屏?...

    相关问题 如何设置计算机黑屏时间?如果W7电脑长时间不移动,为什么不保持黑屏? 右键单击右下角任务栏中的电源图标,更改计划设置,"关闭显示器"和"使计算机进入睡眠&quo ...

  7. w ndows系统开机时黑屏,Windows 7 启动系统后出现黑屏,要怎么解决?

    陈宏斌   2020-6-24 高级软件工程师 概要 电脑黑屏是我们常见的故障之一,那Windows 7启动系统登录后,桌面出现黑屏应该如何解决呢?本文附上原因分析及解决方法. Windows 7启动 ...

  8. android修改系统时系统黑屏时不进入休眠状态

    基于android4.4修改,在frameworks/base/services/java/com/android/server/power/PowerManagerService.java里,查看休 ...

  9. android 录像 视频大小,Android相机 – 录制视频时预览放大

    我一直试图弄清楚一段时间,但由于某种原因,当我开始使用相机录制视频时,预览放大.我从以下示例中获取以下代码: @Override public void surfaceChanged(SurfaceH ...

最新文章

  1. java取网页数据_浅析JAVA实现网页取内容
  2. 大数据小项目之电视收视率企业项目04--完全分布式搭建
  3. [转载]如何做到 jQuery-free?
  4. 微软认知服务开发实践(1) - 牛津计划简介
  5. java 报表_2020 最新流行的Java Web报表工具比对
  6. 开奖|1024中奖名单公布以及Postman资料分享
  7. Java中的继承:父类和子类的关系
  8. 涵盖全网动漫、影视、小说的APP集合,手机有了他们,看遍全网
  9. Magic Swf2Gif(SWF转换GIF)绿色汉化版 V1.35
  10. linux识别riser卡,一种应用在GPU服务器中可灵活配置的Riser卡的制作方法
  11. 面向部件的整车E/E架构开发咨询服务
  12. Towards a new generation of artificial intelligence in China
  13. LSI阵列卡的使用教程
  14. ERROR: Rosdep experienced an error: Unable to handle package.xml format version ‘3‘
  15. 小冰的忍者团队,她在日本开启了怎样一种商业模式?
  16. matlab语音算法,[转载]RLS算法多麦克风语音降噪( matlab编程 )
  17. 【Mongo】.wt文件数据恢复
  18. 2021年金属非金属矿山(地下矿山)安全管理人员考试题库及金属非金属矿山(地下矿山)安全管理人员新版试题
  19. mysql商品平均价、总价
  20. 【算法竞赛模板】质因子、质数、约数、余数、快速幂(数论大全)

热门文章

  1. TI vs Nordic BLE 产品市场分析
  2. 什么是Saas,以及什么是PLG下的Saas
  3. MAC 终端命令,解压 rar 文件配置
  4. Typescript - 安装与配置
  5. NGINX源码之:ngx_bufchain
  6. nginx日志模块ngx_http_log_module源码分析
  7. 自己捣鼓的小程序实现订单代付的功能
  8. 教你百度网盘文件转阿里云
  9. 水平仪算公式计算机,水准仪的使用及计算方法
  10. 微信公众号开发(四)——点击菜单回复图片和语音