美文网首页bug处理
Cursor泄露的处理

Cursor泄露的处理

作者: tiger桂 | 来源:发表于2017-04-27 16:40 被阅读0次

    cursor出现泄露时的log

    cursor泄露的检测

    解决cursor泄露

    1. cursor出现泄露时的log

    如果有cursor未被关闭,在logcat打印出来的log中有可能出现如下的信息:

    例如,异常信息 ERROR/Cursor(line number): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened hereFinalizing a Cursor that has not been deactivated or closed.

    CursorWrapperInner: Cursor finalized without prior close(),这个是一个警告,提醒有未关闭的cursor。

    JavaBinder: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=1000, 打开的cursor太多,导致无法再为新的cursor分配空间。

    还有一种非常隐蔽的形式,如果单个cursor的数据量非常小,即使打开非常多的cursor也不会出现内存不足的情况。但是却会报出Too many open files的错误。在linux系统中,文件描述符是有上限限制的,在android中默认是1024个。如果跨进程调用每个cursor都会打开一块共 享内存,这就需要使用一个文件描述符,当文件描述符资源用尽时,就再也无法打开新的文件。

    2.cursor泄露的检测

    如果加入如下代码,将会在严格模式的(StrictMode)策略中加入检测泄露的策略。

    importandroid.os.StrictMode;

    publicclassTestActivityextendsActivity {

    privatestaticfinalbooleanDEVELOPER_MODE =true;

    publicvoidonCreate() {

    if(DEVELOPER_MODE) {

    StrictMode.setVmPolicy(new VmPolicy.Builder()

    .detectLeakedSqlLiteObjects()

    .detectLeakedClosableObjects()

    .penaltyLog()

    .penaltyDeath()

    .build());}

    super.onCreate();

    }

    }

    如果是framework开发,还可以使用如下代码,效果和上述代码类似。

    closeguard.setEnable(true);

    如果存在没有关闭的cursor,将会抛出如下异常。这样就能很轻松地知道自己的应用是否存在cursor泄露。

    但是,这里并没有报出泄露的cursor更详细的信息,因此仅能知道出现了问题,却不知道问题出现在哪。

    java.lang.Throwable: Explicit termination method 'close' not called

    at dalvik.system.CloseGuard.open(CloseGuard.java:184)

    at android.content.ContentResolver$CursorWrapperInner.(ContentResolver.java:2496)

    at android.content.ContentResolver.query(ContentResolver.java:509)

    at android.content.ContentResolver.query(ContentResolver.java:429)

    at android.content.AsyncQueryHandler$WorkerHandler.handleMessage(AsyncQueryHandler.java:79)

    at android.os.Handler.dispatchMessage(Handler.java:111)

    at android.os.HandlerThread.run(HandlerThread.java:61)

    那么如何发现cursor在哪里泄露呢?一个简单的方法,在打开和关闭的地方打上log,通过log来发现是否成对

    出现打开和关闭的操作。但是当程序逻辑变得非常复杂,尤其是在各种回调函数和多线程运行的环境下时,往往难于

    发现问题。另一个方法是,可以在创建一个全局的hashMap,每次获取新的cursor时将cursor加入map,然后在程序退出

    等适当的时机打印cursor的状态,哪些cursor没有close将一目了然。这个方法能比较快的定位到问题点,但是却要

    找到每一个cursor的调用点,当程序中有非常多的cursor时,找到他们并逐个修改代码也是很痛苦而费时的事情。

    如果是在framework中开发,那么framework倒是提供一个QueryHistory dumpQueryHistory

    3. 解决cursor泄露

    1)最简单的就是使用后要记得调用close方法关闭。

    2)如果cursor生命周期过长,调用的逻辑很复杂,就很容易无法判断关闭的时机。所以应该尽量减少cursor的生命周期,尽快关闭。

    3)有时候cursor的数据必须要在很长时间都要用到,而且调用逻辑就是很复杂,这个问题如何处理?没关系,android提供了一个很方便的类,android.database.MatrixCursor, 这个类用于构造一个cursor在内存中的拷贝,拷贝完后可以直接关闭原来的cursor,而这个内存中的MatrixCursor就不用关心它是否关闭了(除非你又给它设置了回调),它的回收仅仅依赖于java的回收机制。具体如何使用,请看下面这个复制cursor的方法。

    public staticMatrixCursormatrixCursorFromCursor(Cursorcursor) {

    if(cursor ==null) {

    return null;

    }

    String[]columnNames= cursor.getColumnNames();

    if(columnNames==null) {

    columnNames=newString[] {};

    }

    MatrixCursor newCursor=newMatrixCursor(columnNames);

    intnumColumns= cursor.getColumnCount();

    String data[] =newString[numColumns];

    cursor.moveToPosition(-1);

    while(cursor.moveToNext()) {

    for(inti=0;i

    data[i] = cursor.getString(i);

    }

    newCursor.addRow(data);

    }

    returnnewCursor;

    }

    相关文章

      网友评论

        本文标题:Cursor泄露的处理

        本文链接:https://www.haomeiwen.com/subject/bxhnzttx.html