首页 > 解决方案 > CameraX 错误:减少使用计数的次数多于增加次数

问题描述

我正在使用 CameraX 1.0.0-beta01 从服务中拍照。我正在使用 ImageCapture 和 ImageAnalysis - 即没有预览。

我正在实施LifeCycleOwner和调用

mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.STARTED);

当我开始服务和mLifecycleRegistry.markState(Lifecycle.State.DESTROYED) 完成拍照时。

我收到用户报告的以下错误,并非特定于任何 android 版本或设备。

Fatal Exception: java.lang.IllegalStateException: Decrementing use count occurs more times than incrementing
       at androidx.camera.core.impl.DeferrableSurface.decrementUseCount(DeferrableSurface.java:256)
       at androidx.camera.core.impl.DeferrableSurfaces.decrementAll(DeferrableSurfaces.java:173)
       at androidx.camera.camera2.internal.CaptureSession.clearConfiguredSurfaces(CaptureSession.java:539)
       at androidx.camera.camera2.internal.CaptureSession$StateCallback.onClosed(CaptureSession.java:928)
       at androidx.camera.camera2.internal.CaptureSession.forceClose(CaptureSession.java:533)
       at androidx.camera.camera2.internal.Camera2CameraImpl$StateCallback.onDisconnected(Camera2CameraImpl.java:1210)
       at androidx.camera.camera2.internal.CameraDeviceStateCallbacks$ComboDeviceStateCallback.onDisconnected(CameraDeviceStateCallbacks.java:112)
       at androidx.camera.camera2.internal.compat.CameraDeviceCompat$StateCallbackExecutorWrapper$2.run(CameraDeviceCompat.java:121)
       at android.os.Handler.handleCallback(Handler.java:751)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:154)
       at android.os.HandlerThread.run(HandlerThread.java:61)

我自己无法重现该错误。什么可能导致这种情况?


更新:

我找到了DeferrableSurface.java的来源,它发生在这里:


    /**
     * Decrements the use count.
     *
     * <p>If this causes the use count to go to zero and the surface has been closed, this will
     * complete the future returned by {@link #getTerminationFuture()}.
     */
    public void decrementUseCount() {
        // If this gets set, then the surface will terminate
        CallbackToFutureAdapter.Completer<Void> terminationCompleter = null;
        synchronized (mLock) {
            if (mUseCount == 0) {
                throw new IllegalStateException("Decrementing use count occurs more times than "
                        + "incrementing");
            }

            mUseCount--;
            if (mUseCount == 0 && mClosed) {
                terminationCompleter = mTerminationCompleter;
                mTerminationCompleter = null;
            }

            // ...

这是从DeferrableSurfaces.java调用的:


    /**
     * Decrements the usage counts of every surface in the provided list.
     *
     * @param surfaceList The list of surfaces whose usage count should be decremented.
     */
    public static void decrementAll(@NonNull List<DeferrableSurface> surfaceList) {
        for (DeferrableSurface surface : surfaceList) {
            surface.decrementUseCount();
        }
    }

我想当我将当前状态标记为时会调用它DESTROYED。很想说这看起来像一个错误。想法?


更新 2: 我的代码,精简到相关的内容:

public class MyService extends Service implements LifecycleOwner{

    private LifecycleRegistry mLifecycleRegistry;
    public static boolean serviceRunning = false;
    private boolean isCameraOff = true;
    private ImageCapture imageCapture;
    private int pendingImages = 0;

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

    /*  called from activity when we should take a picture.
        can be called several times, and we therefore increment an
        int, keeping track of how many pictures should be taken. */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        serviceRunning = true;

        // Increment pending pictures
        pendingImages++;

        // if camera is not in use, setup camera and take picture, otherwise incrementing pendingImages var is enough
        if(isCameraOff){
            isCameraOff = false;
            cameraSetup(); // takePicture called from the end of setup
        }

        return START_NOT_STICKY;
    }

    private void cameraSetup(){

        // lifecycle for camera
        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);

        ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(() -> {

            try {

                // Camera provider is now guaranteed to be available
                ProcessCameraProvider cameraProvider = (ProcessCameraProvider)cameraProviderFuture.get();

                // Dummy Analyzer
                ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .build();

                // empty analyzer, for the time
                imageAnalysis.setAnalyzer(executor, ImageProxy::close);

                // ImageCapture
                imageCapture = new ImageCapture.Builder()
                        .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                        .setTargetResolution(new Size(900,1200))
                        .build();

                // Select front facing camera
                CameraSelector cameraSelector = new CameraSelector.Builder()
                        .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                        .build();

                // Bind Camera to lifecycle
                cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture, imageAnalysis);

                // Give camera some time to open, 500 ms should do, before proceeding
                Utils.sleepThread(500);

                // take picture
                takePicture();

            } catch (InterruptedException | ExecutionException e) {
                // Currently no exceptions thrown. cameraProviderFuture.get() should
                // not block since the listener is being called, so no need to
                // handle InterruptedException.
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private Executor executor = Executors.newSingleThreadExecutor();
    private void takePicture(){

        // Create new file
        File file = new File(Utils.createFilePath(context, getApplication()));

        // Create Meta data
        ImageCapture.Metadata meta = new ImageCapture.Metadata();
        meta.setReversedHorizontal(false); // don't mirror photos

        // Collect file options
        ImageCapture.OutputFileOptions outputFileOptions =
                new ImageCapture.OutputFileOptions.Builder(file)
                        .setMetadata(meta)
                        .build();

        // Take picture
        imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback(){

            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {

                // got picture

                // Decrement the number of pictures we need to take
                pendingImages--;

                // take another image if pending
                if(pendingImages > 0){
                    takePicture();
                }else{
                    closeSession();
                }

            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                exception.printStackTrace();
                closeSession();
            }
        });
    }

    private void closeSession(){

        // tell lifecycle and thus camera to close (has to be called from main thread)
        // closeSession is called from the camera callback and is not on main thread.
        new Handler(context.getMainLooper()).post(() -> {
            mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
        });

        // mark camera as off
        isCameraOff = true;

        stopSelf(); // stop service
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        serviceRunning = false;
    }

    // Service Stuff

    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}

标签: androidandroid-camerax

解决方案


推荐阅读