当前位置:网站首页>Pit encountered using camera x_ When onpause, the camera is not released, resulting in a black screen when it comes back

Pit encountered using camera x_ When onpause, the camera is not released, resulting in a black screen when it comes back

2022-04-23 19:14:00 cjzcjl

Preface :

         Recently, in order to realize the work content of photography , Think it over and decide to use Camera X As a photo of this project API, The main reasons are :1、API There is no in use Camera V2 API The amount of code required is large , Although I have relevant working experience , But think of the amount of code , Compared with Camera X Example , I still don't want to use .2、 Better compatibility , And provide common API Basically meet the requirements , Image focusing 、 Flash, etc API Have it all. , More suitable for Demo Quickly build .

problem :

         In my opinion Android developer The examples introduced in have their own demo After building , I am right. Camera X Have a certain understanding of the application of . But found OnPause Then come back , The preview is lost . So I thought of two possibilities :

1、PreviewView Problems caused by possible destruction .

2、 Related to the life cycle of the camera , I may not have used it well .

Start with the first possibility . Try to OnResume Call the following code when :

        if (mPreview != null) {
            // Add... To the preview window surface passageway 
            mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
        }

         exactly OnResume Then the preview screen can come back , But still have to wait 5 Second . So doubt is more likely and possible 2 of .

         As before Camera V2 Experience , If the camera resource is not released, it will cause some exceptions , In general, other applications that need to call the camera will also have problems . So I opened my demo, Then open wechat to scan , I found that the preview after scanning is also black 5 Seconds before it comes out , More in line with the above intuition and experience . So in OnPause Call in unBindAll Release the camera ,OnResume Reinitialize and set the preview window , The problem was solved .

Detailed code :

Camera logic :

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;
    /** Camera **/
    private ImageCapture mImageCapture;
    private ExecutorService mTakePhotoExecutor;
    private Button mBtnTakePhoto;
    private ImageView mImagePhoto;
    private Preview mPreview;
    private Button mBtnFlashLight;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Special thread for photographing , Let it not jam the main thread :
        mTakePhotoExecutor = Executors.newSingleThreadExecutor();
        // Permission to apply for 
        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);
        // Photo button 
        mBtnTakePhoto.setOnClickListener(v -> {
            takePhoto(mImageCapture);
        });
        // The flash 
        mBtnFlashLight.setOnClickListener(v -> {
            switch ((String) v.getTag()) {
                case "auto":
                    mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_ON);
                    ((Button) v).setText(" The flash : Normally open ");
                    v.setTag("on");
                    break;
                case "on":
                    mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_OFF);
                    ((Button) v).setText(" The flash : close ");
                    v.setTag("off");
                    break;
                default:
                    mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);
                    ((Button) v).setText(" The flash : Automatically ");
                    v.setTag("auto");
                    break;
            }
        });
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        bindCameraUseCases(mCameraPRrovider, null);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @Override
    protected void onResume() {
        super.onResume();
        ListenableFuture<ProcessCameraProvider> processCameraProvider = ProcessCameraProvider.getInstance(this);
        // When you come back, you need to rebind :
        processCameraProvider.addListener(() -> {
            try {
                mCameraPRrovider = processCameraProvider.get();
                // Bind preview window, etc 
                bindCameraUseCases(mCameraPRrovider, null);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, getMainExecutor()); // Only in the main thread 
        mLifecycleRegistry.markState(Lifecycle.State.RESUMED);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Release camera resources when you go out 
        mCameraPRrovider.unbindAll();
    }

    @Override
    protected 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;
        // Preview interface :
        mPreview = new Preview.Builder()
                .setTargetAspectRatio(aspectRatio)
                .setTargetRotation(mPreviewView.getDisplay().getRotation())
                .build();
        // Add... To the preview window surface passageway 
        mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
        // Photo capture :
        mImageCapture = new ImageCapture.Builder()
                .setTargetAspectRatio(aspectRatio)
                // Low delay photography , The reaction speed will be faster 
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .setTargetRotation(mPreviewView.getDisplay().getRotation())
                .setFlashMode(ImageCapture.FLASH_MODE_AUTO) // Flash adjustment 
                .build();
        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setTargetAspectRatio(aspectRatio)
                .setTargetRotation(mPreviewView.getDisplay().getRotation())
                .build();
        // Binding relationships that may exist before unbinding 
        cameraProvider.unbindAll();
        // If there is a rear camera, choose the rear , Otherwise, use the front :
        if (hasBackCamera()) {
            mLensFacing = CameraSelector.LENS_FACING_BACK;
        } if (hasFrontCamera()) {
//            mLensFacing = CameraSelector.LENS_FACING_FRONT;
        } else {
            throw new IllegalStateException(" Neither front nor rear Cameras ");
        }
        CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(mLensFacing).build();

        // Binding lifecycle 、 Preview window 、 Camera acquisition device, etc 
        cameraProvider.bindToLifecycle(this,
                cameraSelector, mPreview, mImageCapture, imageAnalysis);
    }

    /** Actual photo logic **/
    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);
        // Save it to APP Local folder :
//        File cacheFileDir = getCacheDir(); //APP Inside cache Address 
        File cacheFileDir = getExternalCacheDir(); // Shared 、 Everyone can visit cache Address 
        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();
                // After the photo is taken, it is received from the callback 
                imageCapture.takePicture(outputOptions, mTakePhotoExecutor, new ImageCapture.OnImageSavedCallback() {

                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        Uri savedUri = outputFileResults.getSavedUri() == null ?
                                Uri.fromFile(newFile) :
                                outputFileResults.getSavedUri();
                        // Get a picture of the button 
                        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+ API
                        if (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 statement
                        if (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() {
                                    @Override
                                    public void onScanCompleted(String path, Uri uri) {
                                        Log.i("cjztest", " New file scan complete ,  file size :" + newFile.length());
                                    }
                                });
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Log.e("cjztest", " Photo failed ");
                    }
                });
            } catch (IOException e) {
                Log.e("cjztest", " Failed to create file ");
                e.printStackTrace();
            }
        }
        Log.i("cjztest", "cacheFileDir:" + cacheFileDir);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

Interface description :

<?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">

    <Button
        android:id="@+id/btn_flash_light"
        android:text=" The flash : Automatically "
        android:tag="auto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|top"/>

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

    <ImageView
        android:id="@+id/iv_photo"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="bottom|left"
        android:scaleType="fitXY"/>

    <Button
        android:id="@+id/btn_take_photo"
        android:text=" Taking pictures "
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="bottom|center"
        />

</FrameLayout>

Gitee Address :

lvlv/AndroidDemo Complete works of - Gitee.com

版权声明
本文为[cjzcjl]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204210600587496.html