public abstract class

DeferrableSurface

extends java.lang.Object

 java.lang.Object

↳androidx.camera.core.DeferrableSurface

Subclasses:

ImmediateSurface

Overview

A reference to a whose creation can be deferred to a later time.

A DeferrableSurface.OnSurfaceDetachedListener can also be set to be notified of surface detach event. It can be used to safely close the surface.

Summary

Fields
protected static final java.lang.StringTAG

Constructors
publicDeferrableSurface()

Methods
public final voidclose()

Close the surface.

public intgetAttachedCount()

public final <any>getSurface()

Returns a that is wrapped in a .

public voidnotifySurfaceAttached()

Notifies this surface is attached

public voidnotifySurfaceDetached()

Notifies this surface is detached.

public voidsetOnSurfaceDetachedListener(java.util.concurrent.Executor executor, DeferrableSurface.OnSurfaceDetachedListener listener)

Sets the listener to be called when surface is detached totally.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

protected static final java.lang.String TAG

Constructors

public DeferrableSurface()

Methods

public final <any> getSurface()

Returns a that is wrapped in a .

Returns:

Will return a with an exception if the DeferrableSurface is already closed.

public void notifySurfaceAttached()

Notifies this surface is attached

public final void close()

Close the surface.

After closing, DeferrableSurface.getSurface() will return a DeferrableSurface.SurfaceClosedException.

public void notifySurfaceDetached()

Notifies this surface is detached. OnSurfaceDetachedListener will be called if it is detached totally

public void setOnSurfaceDetachedListener(java.util.concurrent.Executor executor, DeferrableSurface.OnSurfaceDetachedListener listener)

Sets the listener to be called when surface is detached totally.

If the surface is currently not attached, the listener will be called immediately. When clearing resource like ImageReader, to close it safely you can call this method and close the resources in the listener. This can ensure the surface is closed after it is no longer held in camera.

public int getAttachedCount()

Source

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.core;

import android.util.Log;
import android.view.Surface;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.core.util.Preconditions;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * A reference to a {@link Surface} whose creation can be deferred to a later time.
 *
 * <p>A {@link OnSurfaceDetachedListener} can also be set to be notified of surface detach event. It
 * can be used to safely close the surface.
 *
 * @hide
 */
@RestrictTo(Scope.LIBRARY_GROUP)
public abstract class DeferrableSurface {
    /**
     * The exception that is returned by the ListenableFuture of {@link #getSurface()} if the
     * {@link Surface} backing the DeferrableSurface has already been closed.
     *
     * @hide
     */
    @RestrictTo(Scope.LIBRARY_GROUP)
    public static final class SurfaceClosedException extends Exception {
        DeferrableSurface mDeferrableSurface;

        public SurfaceClosedException(@NonNull String s, @NonNull Throwable e,
                @NonNull DeferrableSurface surface) {
            super(s, e);
            mDeferrableSurface = surface;
        }

        public SurfaceClosedException(@NonNull String s, @NonNull DeferrableSurface surface) {
            super(s);
            mDeferrableSurface = surface;
        }

        /**
         * Returns the {@link DeferrableSurface} that generated the exception.
         *
         * <p>The deferrable surface will already be closed.
         */
        @NonNull
        public DeferrableSurface getDeferrableSurface() {
            return mDeferrableSurface;
        }
    }

    private static final boolean DEBUG = false;
    protected static final String TAG = "DeferrableSurface";

    // Debug only, used to track total count of attached surfaces.
    private static AtomicInteger sSurfaceCount = new AtomicInteger(0);

    // The count of attachment.
    @GuardedBy("mLock")
    private int mAttachedCount = 0;

    private volatile boolean mClosed = false;

    // Listener to be called when surface is detached totally.
    @Nullable
    @GuardedBy("mLock")
    private OnSurfaceDetachedListener mOnSurfaceDetachedListener = null;

    @Nullable
    @GuardedBy("mLock")
    private Executor mListenerExecutor = null;

    // Lock used for accessing states.
    private final Object mLock = new Object();

    /**
     * Returns a {@link Surface} that is wrapped in a {@link ListenableFuture}.
     *
     * @return Will return a {@link ListenableFuture} with an exception if the DeferrableSurface
     * is already closed.
     */
    @NonNull
    public final ListenableFuture<Surface> getSurface() {
        synchronized (mLock) {
            if (mClosed) {
                return Futures.immediateFailedFuture(
                        new SurfaceClosedException("DeferrableSurface already closed.", this));
            }
            return provideSurface();
        }
    }

    /**
     * Returns a {@link Surface} that is wrapped in a {@link ListenableFuture} when the
     * DeferrableSurface has not yet been closed.
     */
    abstract ListenableFuture<Surface> provideSurface();

    /** Notifies this surface is attached */
    public void notifySurfaceAttached() {
        synchronized (mLock) {
            mAttachedCount++;

            if (DEBUG) {
                if (mAttachedCount == 1) {
                    Log.d(TAG, "Surface attached, count = " + sSurfaceCount.incrementAndGet() + " "
                            + this);
                }
                Log.d(TAG, "attach count+1, attachedCount=" + mAttachedCount + " " + this);
            }
        }
    }

    /**
     * Close the surface.
     *
     * <p>After closing, {@link #getSurface()} will return a {@link SurfaceClosedException}.
     */
    public final void close() {
        synchronized (mLock) {
            mClosed = true;
        }
    }

    /**
     * Notifies this surface is detached. OnSurfaceDetachedListener will be called if it is detached
     * totally
     */
    public void notifySurfaceDetached() {
        OnSurfaceDetachedListener listener = null;
        Executor listenerExecutor = null;

        synchronized (mLock) {
            if (mAttachedCount == 0) {
                throw new IllegalStateException("Detaching occurs more times than attaching");
            }

            mAttachedCount--;
            if (mAttachedCount == 0) {
                listener = mOnSurfaceDetachedListener;
                listenerExecutor = mListenerExecutor;
            }

            if (DEBUG) {
                Log.d(TAG, "attach count-1,  attachedCount=" + mAttachedCount + " " + this);

                if (mAttachedCount == 0) {
                    Log.d(TAG, "Surface detached, count = " + sSurfaceCount.decrementAndGet() + " "
                            + this);
                }
            }
        }

        if (listener != null && listenerExecutor != null) {
            callOnSurfaceDetachedListener(listener, listenerExecutor);
        }
    }

    /**
     * Sets the listener to be called when surface is detached totally.
     *
     * <p>If the surface is currently not attached, the listener will be called immediately. When
     * clearing resource like ImageReader, to close it safely you can call this method and close the
     * resources in the listener. This can ensure the surface is closed after it is no longer held
     * in camera.
     */
    public void setOnSurfaceDetachedListener(@NonNull Executor executor,
            @NonNull OnSurfaceDetachedListener listener) {
        Preconditions.checkNotNull(executor);
        Preconditions.checkNotNull(listener);
        boolean shouldCallListenerNow = false;
        synchronized (mLock) {
            mOnSurfaceDetachedListener = listener;
            mListenerExecutor = executor;
            // Calls the listener immediately if the surface is not attached right now.
            if (mAttachedCount == 0) {
                shouldCallListenerNow = true;
            }
        }

        if (shouldCallListenerNow) {
            callOnSurfaceDetachedListener(listener, executor);
        }
    }

    private static void callOnSurfaceDetachedListener(
            @NonNull final OnSurfaceDetachedListener listener, @NonNull Executor executor) {
        Preconditions.checkNotNull(executor);
        Preconditions.checkNotNull(listener);

        executor.execute(new Runnable() {
            @Override
            public void run() {
                listener.onSurfaceDetached();
            }
        });
    }

    /** @hide */
    @RestrictTo(Scope.TESTS)
    public int getAttachedCount() {
        synchronized (mLock) {
            return mAttachedCount;
        }
    }

    /**
     * The listener to be called when surface is detached totally.
     */
    public interface OnSurfaceDetachedListener {
        /**
         * Called when surface is totally detached.
         */
        void onSurfaceDetached();
    }
}