谨慎使用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也不仅限于此,还有其他的共享内存、自己代码的业务内存等,要多方面综合考虑