Android的Cursor的close方法不调用会不会造成内存泄露

研究内存问题时,突然想到Android的Cursor的Close如果不调用会不会造成内存泄露。
于是翻开Android源码一番跟踪,发现google已经为开发者加了一层保证,即时不调用Close方法,也不会造成内存泄露。
即使如此,开发者最好还是自己调用close进行Cursor的资源释放。
下面看分析流程:

一般我们都是通过context的openOrCreateDatabase方法得到SQLiteDatabase对象进行数据库操作。
WrapContext->openOrCreateDatabase("city.db", Context.MODE_PRIVATE, null)具体实现在ContextImpl中。

第三个参数为CursorFactory对象,并且此对象决定返回的Cursor类型。
如果CursorFactory不传入null,需要调用openOrCreateDatabase的地方实现CursorFactory
在看CursorFactory这个接口,代码在SQLiteDatabase中

[java]  view plain  copy
  1. /** 
  2.  * Used to allow returning sub-classes of {@link Cursor} when calling query. 
  3.  */  
  4. public interface CursorFactory {  
  5.     /** 
  6.      * See 
  7.      * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}. 
  8.      */  
  9.     public Cursor newCursor(SQLiteDatabase db,  
  10.             SQLiteCursorDriver masterQuery, String editTable,  
  11.             SQLiteQuery query);  
  12. }  


所以必须要实现newCursor方法。
再看传入null时Android系统的实现,具体实现在SQLiteDirectCursorDriver这个类中

[java]  view plain  copy
  1. public Cursor query(CursorFactory factory, String[] selectionArgs) {  
  2.     // Compile the query  
  3.     SQLiteQuery query = null;  
  4.     try {  
  5.         mDatabase.lock(mSql);  
  6.         mDatabase.closePendingStatements();  
  7.         query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);  
  8.         // Create the cursor  
  9.         if (factory == null) {  
  10.             mCursor = new SQLiteCursor(this, mEditTable, query);  
  11.         } else {  
  12.             mCursor = factory.newCursor(mDatabase, this, mEditTable, query);  
  13.         }  
  14.         mQuery = query;  
  15.         query = null;  
  16.         return mCursor;  
  17.     } finally {  
  18.         // Make sure this object is cleaned up if something happens  
  19.         if (query != null) query.close();  
  20.         mDatabase.unlock();  
  21.     }  
  22. }  


看红色部分即为系统的处理方式,如果应用程序自己实现CursorFactory就会调用应用程序实现的
newCursor方法。如果应用不实现,那么就会返回 SQLiteCursor这个对象, SQLiteCursor间接实现了
Cursor接口,这个 Cursor对象即应用中通常使用的 Cursor

再看 SQLiteCursor的构造方法
[java]  view plain  copy
  1. query.mDatabase.lock(query.mSql);  
  2. try {  
  3.     // Setup the list of columns  
  4.     int columnCount = mQuery.columnCountLocked();  
  5.     mColumns = new String[columnCount];  
  6.     // Read in all column names  
  7.     for (int i = 0; i < columnCount; i++) {  
  8.         String columnName = mQuery.columnNameLocked(i);  
  9.         mColumns[i] = columnName;  
  10.         if (false) {  
  11.             Log.v("DatabaseWindow""mColumns[" + i + "] is "  
  12.                     + mColumns[i]);  
  13.         }  
  14.   
  15.         // Make note of the row ID column index for quick access to it  
  16.         if ("_id".equals(columnName)) {  
  17.             mRowIdColumnIndex = i;  
  18.         }  
  19.     }  
  20. finally {  
  21.     query.mDatabase.unlock();  
  22. }  

可以看到在构造方法里面会通过 SQLiteQuery去查询数据库数据生成 Cursor数据。
至此带数据Cursor就生成了,并且知道Cursor具体是实现在什么地方。

最后再看一下 SQLiteCursor里的 finalize方法,finalize是java GC机制会调用到此方法,
java中并不建议重写这个方法,但是这里为什么还要重写呢,目的就是释放native的C++资源。
看代码:
    /**
     *  Release the native resources, if they haven't been released yet.
     */
    @Override
    protected void finalize() {
        try {
            // if the cursor hasn't been closed yet, close it first
            if (mWindow != null) {
                if (StrictMode.vmSqliteObjectLeaksEnabled()) {
                    int len = mQuery.mSql.length();
                    StrictMode.onSqliteObjectLeaked(
                        "Finalizing a Cursor that has not been deactivated or closed. " +
                        "database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
                        ", query = " + mQuery.mSql.substring(0, (len > 1000) ? 1000 : len),
                        mStackTrace);
                }
                close();
                SQLiteDebug.notifyActiveCursorFinalized();
            } else {
                if (false) {
                    Log.v(TAG, "Finalizing cursor on database = " + mQuery.mDatabase.getPath() +
                            ", table = " + mEditTable + ", query = " + mQuery.mSql);
                }
            }
        } finally {
            super.finalize();
        }
    }


现在知道为什么很多时候我们写代码一次次的查询又没有调用cursor的close()方法,却没有造成内存泄露了吧,
应该google帮我们做了一层保障,就是当cursor用完了之后,并且已经没有没有引用指向Cursor( 很多时候
习惯都是在查询完之后,在某个方法体中对Cursor进行遍历并生成list),这个这个Cursor就再也没有引用指向他,
那么下一次GC的时候就会调用到finalize方法,这个时候就会执行close了。

但是万一什么时候自己实现CursorFactory并实现Cursor的时候,又没有覆盖finalize方法,同时又没有在程序中主动调用
close方法进行native资源释放,就会造成内存泄露。

写代码的时候Cursor使用完,最好主动调用close方法,因为cursor不关闭虽然不会造成内存泄漏,
但是会耗尽IO资源(虚拟机并没有执行GC,又有新的cursor操作,导致cursor的finalize方法并没有执行,

就申请新的IO资源,会直接在native crash),。


原文地址:http://blog.csdn.net/c16882599/article/details/16335459


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值