谨慎使用android.view.SurfaceView.setVisibility方法

谨慎使用android.view.SurfaceView.setVisibility方法

why?

原因就是此方法会导致native的内存暴增,前提条件你使用surfaceview去绘制一些复杂的3D图形,绘制复杂、业务复杂的场景;
setVisibility方法会调用surfaceView的updateSurface,而此方法内部就可能会分配大量的native内存

分析

查看 updateSurface源码:

 /** @hide */
protected void updateSurface() {
	final boolean formatChanged = mFormat != mRequestedFormat;
    final boolean visibleChanged = mVisible != mRequestedVisible;
    final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
           && mRequestedVisible;

	.....
	if (creating) {
       viewRoot.createBoundsSurface(mSubLayer);
           mSurfaceSession = new SurfaceSession();
           mDeferredDestroySurfaceControl = mSurfaceControl;

           updateOpaqueFlag();
          final String name = "SurfaceView - " + viewRoot.getTitle().toString();

           mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
               .setName(name)
               .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
               .setBufferSize(mSurfaceWidth, mSurfaceHeight)
               .setFormat(mFormat)
               .setParent(viewRoot.getSurfaceControl())
               .setFlags(mSurfaceFlags)
               .build();
           mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
               .setName("Background for -" + name)
               .setOpaque(true)
               .setColorLayer()
               .setParent(mSurfaceControl)
               .build();

       } else if (mSurfaceControl == null) {
          return;
       }
       .....
}

如上函数,重点在于满足creating为true的情况下new SurfaceControl.Builder,在这个builder里面,会直接创建SurfaceControl对象,在SurfaceControl构造方法中有一个:

private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
              SurfaceControl parent, SparseIntArray metadata){
	mNativeObject = nativeCreate(session, name, w, h, format, flags,
                      parent != null ? parent.mNativeObject : 0, metaParcel);
}

nativeCreate是一个native方法,在native层的代码如下:

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ....
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
    ......
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
   .....
    return reinterpret_cast<jlong>(surface.get());
}

status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     SurfaceControl* parent,
                                                     LayerMetadata metadata) {
    sp<SurfaceControl> sur;
    status_t err = mStatus;
	if (mStatus == NO_ERROR) {
	       sp<IBinder> handle;
	       sp<IBinder> parentHandle;
	       sp<IGraphicBufferProducer> gbp;
	
	       if (parent != nullptr) {
	           parentHandle = parent->getHandle();
	       }
        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp);
        .....
    }
    return err;
}

到这里就不继续往下分析,不过也不难看出就是要重新创建一个surface,上面的createSurfaceChecked方法中的“IGraphicBufferProducer”代码就是分配对应的图像内存的例证;所以从上surfaceView的setVisibility方法很有可能就在native层分配内存,如果频繁调用setVisibility方法,而又没做好回收工作,就极有可能使内存暴增,系统和应用奔溃,出现android.view.Surface$OutOfResourcesException异常

如果频繁setVisiviblity方法分配内存,系统会将当前surface对象之前分配的内存回收掉吗?

看了SurfaceView的updateSurface函数,里面有将之前创建的SurfaceControl回收掉,但是回收掉有一些列条件,比较复杂,我也没去深入的研究,有兴趣的可以自行去分析;

总结:在项目中如果没有存在内存泄露,而内存暴增又来源于native,可以参考是否来源于surfaceview;当然native也不仅限于此,还有其他的共享内存、自己代码的业务内存等,要多方面综合考虑

你可能感兴趣的:(bug分析,android)