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函数已分析完毕,不知读者看完整个流程后有何感想。