Surface在Android上进行分区后如何处理垃圾回收?

Leo*_*ilä 4 java java-native-interface android garbage-collection parcelable

我正在使用源代码Surface.java作为此问题的参考.

Surface实现了Parcelable接口,它还保存了本机端对象的句柄.

我很想知道在这种情况下如何处理垃圾收集:

  1. 创建曲面(A)并将其写入包裹.之后没有提到它.

  2. 从包裹中读取原始表面(B)的副本; 让我们说这发生在另一个用于渲染的线程上.此实例现在保持与(A)相同的本机句柄,并且在某处对此实例有强引用.

  3. 发生GC并收集(A),因为它不再被引用.finalize()运行,调用release(),然后调用nativeRelease(long)本机句柄.

粗略地看一下源代码让我觉得现在(B)也应该踢掉桶并停止工作,因为本机句柄被释放了,但是在尝试复制之后似乎并非如此.(A)确实被收集但(B)继续存在并仍然可用.

现在我感觉有一些引用计数继续使用本机对象,或者在parcelling进程的本机端进行其他一些魔术.

无论我的假设是否正确,我都在寻找导致此行为的原因的概述,最好是对框架源代码的一些引用.我也对表面锁定在类似情况下的工作方式感兴趣.

use*_*723 8

Surface只是BufferQueue的引用.它们包含一个Binder令牌,用于协商在生产者和接收者之间发送图形缓冲区.相关的JNI代码:

static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) {
  Parcel* parcel = parcelForJavaObject(env, parcelObj);
  if (parcel == NULL) {
    doThrowNPE(env);
    return 0;
  }

  android::view::Surface surfaceShim;

  // Calling code in Surface.java has already read the name of the Surface
  // from the Parcel
  surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);

  sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));

  // update the Surface only if the underlying IGraphicBufferProducer
  // has changed.
  if (self != nullptr
        && (IInterface::asBinder(self->getIGraphicBufferProducer()) ==
                IInterface::asBinder(surfaceShim.graphicBufferProducer))) {
      // same IGraphicBufferProducer, return ourselves
      return jlong(self.get());
  }

  sp<Surface> sur;
  if (surfaceShim.graphicBufferProducer != nullptr) {
    // we have a new IGraphicBufferProducer, create a new Surface for it
    sur = new Surface(surfaceShim.graphicBufferProducer, true);
    // and keep a reference before passing to java
    sur->incStrong(&sRefBaseOwner);
  }

  if (self != NULL) {
    // and loose the java reference to ourselves
    self->decStrong(&sRefBaseOwner);
  }

  return jlong(sur.get());
}
Run Code Online (Sandbox Code Playgroud)

您可以清楚地看到,如何从Parcel读取Binder令牌并将其转换为IGraphicBufferProducer IPC接口.

Binder标记在内核中被引用计数,只要存在更多,就会破坏用户空间引用之一.

当您在同一个进程中时,锁定语义不会更改,因为本机Surface 维护实例的缓存:

sp<Surface> Surface::readFromParcel(const Parcel& data) {
  Mutex::Autolock _l(sCachedSurfacesLock);
  sp<IBinder> binder(data.readStrongBinder());
  sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
  if (surface == 0) {
   surface = new Surface(data, binder);
   sCachedSurfaces.add(binder, surface);
  } else {
    // The Surface was found in the cache, but we still should clear any
    // remaining data from the parcel.
    data.readStrongBinder();  // ISurfaceTexture
    data.readInt32();         // identity
  }
  if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
    surface = 0;
  }
  cleanCachedSurfacesLocked();
  return surface;
}
Run Code Online (Sandbox Code Playgroud)

Surface在同一进程中通过parcelling/unparcelling创建的每个Java 实例都引用相同的本机Surface,这意味着锁应该仍然有效:如果发生争用,您将获得异常.

尝试同时从多个进程绘制到未分类的Surface会失败,因为IGraphicBufferProducer合同明确禁止:

// connect attempts to connect a client API to the IGraphicBufferProducer.
// This must be called before any other IGraphicBufferProducer methods are
// called except for getAllocator.
//
// This method will fail if the connect was previously called on the
// IGraphicBufferProducer and no corresponding disconnect call was made.
//
// outWidth, outHeight and outTransform are filled with the default width
// and height of the window and current transform applied to buffers,
// respectively. The token needs to be any binder object that lives in the
// producer process -- it is solely used for obtaining a death notification
// when the producer is killed.
virtual status_t connect(const sp<IBinder>& token,
        int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;
Run Code Online (Sandbox Code Playgroud)

您可以在Android网站找到有关设备和固件制造商的更低级图形堆栈架构的更多详细信息.