7.5 Cursor close函数实现分析

虽然大部分Java程序员对主动回收资源(包括内存等)的认识普遍不如C/C++程序员,但是对于Cursor这种重型资源(不仅占用了一个文件描述符,还共享了一块2MB的内存)来说,Java程序员在编程过程中务必显式调用close函数以释放这些资源。在日常工作中,笔者已无数次碰到因为Cursor未关闭而导致Monkey测试未通过的情况了。

另外,有些同事曾问笔者,虽然没有显式调用close函数,但这个对象也没有地方再引用它了。按照Java垃圾回收机制的规矩,该对象在一定时间后会被回收。既然Cursor本身都被回收了,为什么它包含的资源(指CursorWindow)却没有被回收呢?关于这个问题,本节最后再作讨论。

建议 如果写代码时不重视资源回收,等最后出问题时再来查找泄露点的做法,会极大地增加了软件开发的成本。虽然Java提供了垃圾回收机制,但希望读者不要把资源回收的意识丢失。

下面来分析Cursor close函数,先从客户端说起。

7.5.1 客户端close的分析

客户端拿到的游标对象的真实类型是CursorWrapperInner,其close函数的代码如下:

[—>ContentResolver. java:CursorWrapperInner.close]


public void close(){

super.close();//调用基类的close函数

//撤销客户端进程和目标CP进程的亲密关系

ContentResolver.this.releaseProvider(mContentProvider);

mProviderReleased=true;

if(mCloseGuard!=null)mCloseGuard.close();

}


下面来看CursorWrapperInner的基类CursorWrapper的close函数。这里必须提醒读者,后文对函数分析会频繁从基类转到子类,又从子类转到基类。造成这种局面是因为对类封装得太厉害。

[—>CursorWrapper. java:close]


public void close(){

mCursor.close();//mCursor指向BulkCursorToCursorAdaptor

}


BulkCursorToCursorAdaptor close的代码如下:

[—>BulkCurosrToCursorAdaptor. java:close]


public void close(){

super.close();//①再次调用基类close函数,该函数会释放本地创建的CursorWindow资源

if(mBulkCursor!=null){

try{

mBulkCursor.close();//②调用远端对象的close函数

}……

finally{

mBulkCursor=null;

}

}

}


对于Cursor close函数来说,笔者更关注其中所包含的CursorWindow资源是如何释放的。根据以上代码中的注释可知,BulkCurosrToCursorAdaptor的close调用的基类close函数会释放CursorWindow。

接下来来看super.close这个函数。这个close由BulkCurosrToCursorAdaptor的父类Abstract-WindowedCursor的父类AbstractCursor实现,其代码如下:

[—>AbstractCursor. java:close]


public void close(){

mClosed=true;

mContentObservable.unregisterAll();

onDeactivateOrClose();//调用子类实现的onDeactivateOrClose函数

}


onDeactivateOrClose由AbstractCursor的子类AbstractWindowedCursor实现,代码如下:

[—>AbstractWindowedCursor. java:onDeactivateOrClose]


protected void onDeactivateOrClose(){


//调用基类即AbstractCursor的onDeactivateOrClose函数

super.onDeactivateOrClose();

closeWindow();//释放CursorWindow资源

}


close函数涉及的调用居然在派生树中如此反复出现,这让人很无奈!后面还会碰到类似的做法,读者务必保持镇静。

[—>AbstractWindowedCursor. java:close]


protected void closeWindow(){

if(mWindow!=null){

mWindow.close();//调用CurosrWindow的close函数

mWindow=null;

}

}


CursorWindow从SQLiteClosable派生,根据前面的介绍,释放SQLiteClosable代表的资源会利用引用计数来控制,这是如何实现的呢?来看代码:

[—>CursorWindow. java:close]


public void close(){

releaseReference();//减少一次引用计数

}


releaseReference由CursorWindow的基类SQLiteClosable实现,其代码如下:

[—>SQLiteClosable.java:releaseReference]


public void releaseReference(){

boolean refCountIsZero=false;

synchronized(this){

refCountIsZero=—mReferenceCount==0;

}

if(refCountIsZero){//当引用计数减为0时,就可以真正释放资源了

onAllReferencesReleased();//调用子类实现的onAllReferencesReleased函数

}

}


资源释放的工作由子类实现的onAllReferencesReleased完成,对CursorWindow来说,该函数的代码如下:

[—>CursorWindow. java:onAllReferencesReleased]


protected void onAllReferencesReleased(){

dispose();//调用dispose

}

private void dispose(){

if(mCloseGuard!=null){

mCloseGuard.close();

}

if(mWindowPtr!=0){

recordClosingOfWindow(mWindowPtr);

//调用nativeDispose函数,该函数内部会释放Native层的CursorWindow对象

//至此,客户端获取的那块共享内存就彻底释放干净了

nativeDispose(mWindowPtr);

mWindowPtr=0;

}

}


至此,客户端游标对象的close函数已分析完毕,不知读者看完整个流程后有何感想。