zoukankan      html  css  js  c++  java
  • Android 2.3 AsyncQueryHandler Cursor内存泄漏问题

        AsyncQueryHandler是一个经典的分析Cursor 内存泄漏的例子。
        AsyncQueryHandler: http://developer.android.com/reference/android/content/AsyncQueryHandler.html
       

    下面这段代码是Android2.3系统中Mms信息主页面ConversationList源码的一部分,看看Cursor关闭的情况。

    private final class ThreadListQueryHandler extends AsyncQueryHandler {
        public ThreadListQueryHandler(ContentResolver contentResolver) {
            super(contentResolver);
        }
     
        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            switch (token) {
            case THREAD_LIST_QUERY_TOKEN:
                mListAdapter.changeCursor(cursor);
                setTitle(mTitle);
                ... ...
                break;
     
            case HAVE_LOCKED_MESSAGES_TOKEN:
                long threadId = (Long)cookie;
                confirmDeleteThreadDialog(new DeleteThreadListener(threadId, mQueryHandler,
                        ConversationList.this), threadId == -1,
                        cursor != null && cursor.getCount() > 0,
                        ConversationList.this);
                break;
     
            default:
                Log.e(TAG, "onQueryComplete called with unknown token " + token);
            }
        }
    }
     
    @Override
    protected void onStop() {
        super.onStop();
     
        mListAdapter.changeCursor(null);
    }

    附上CursorAdapter的changeCursor()方法源码:

    /**
     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
     * closed.
     *
     * @param cursor The new cursor to be used
     */
    public void changeCursor(Cursor cursor) {
        Cursor old = swapCursor(cursor);
        if (old != null) {
            old.close();
        }
    }
     
    /**
     * Swap in a new Cursor, returning the old Cursor.  Unlike
     * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
     * closed.
     *
     * @param newCursor The new cursor to be used.
     * @return Returns the previously set Cursor, or null if there wasa not one.
     * If the given new Cursor is the same instance is the previously set
     * Cursor, null is also returned.
     */
    public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        Cursor oldCursor = mCursor;
        if (oldCursor != null) {
            if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
            if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
        }
        mCursor = newCursor;
        if (newCursor != null) {
            if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
            if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;
            // notify the observers about the new cursor
            notifyDataSetChanged();
        } else {
            mRowIDColumn = -1;
            mDataValid = false;
            // notify the observers about the lack of a data set
            notifyDataSetInvalidated();
        }
        return oldCursor;
    }
     

    主要是两点:

        (1). THREAD_LIST_QUERY_TOKEN分支的Cursor正确关闭了吗?
        (2). HAVE_LOCKED_MESSAGES_TOKEN分支的Cursor正确关闭了吗?
       

    根据分析是:
        (1). THREAD_LIST_QUERY_TOKEN分支的Cursor被传递到了mListAdapter了,而mListAdapter在onStop里面使用changeCursor(null),当用户离开当前Activity,这个Cursor被正确关闭了,不会泄露。
        (2). HAVE_LOCKED_MESSAGES_TOKEN分支的Cursor(就是参数cursor),只是作为一个判断的一个条件,被使用后不再使用,但是也没有关掉,所以cursor泄露,在StrictMode监视下只要跑到这个地方都会抛出这个错误:

    E/StrictMode(639): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
    E/StrictMode(639): java.lang.Throwable: Explicit termination method 'close' not called
    E/StrictMode(639): at dalvik.system.CloseGuard.open(CloseGuard.java:184)
    ... ...
     

      在Android4.0 JellyBean中谷歌修正了这个泄露问题,相关代码如下:

    private final class ThreadListQueryHandler extends ConversationQueryHandler {
        public ThreadListQueryHandler(ContentResolver contentResolver) {
            super(contentResolver);
        }
     
        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            switch (token) {
            case THREAD_LIST_QUERY_TOKEN:
                mListAdapter.changeCursor(cursor);
     
                ... ...
     
                break;
     
            case UNREAD_THREADS_QUERY_TOKEN:
                // 新增的UNREAD_THREADS_QUERY_TOKEN分子和HAVE_LOCKED_MESSAGES_TOKEN分支也是类似的情况,cursor在jellybean中被及时关闭了
                int count = 0;
                if (cursor != null) {
                    count = cursor.getCount();
                    cursor.close();
                }
                mUnreadConvCount.setText(count > 0 ? Integer.toString(count) : null);
                break;
     
            case HAVE_LOCKED_MESSAGES_TOKEN:
                @SuppressWarnings("unchecked")
                Collection<Long> threadIds = (Collection<Long>)cookie;
                confirmDeleteThreadDialog(new DeleteThreadListener(threadIds, mQueryHandler,
                        ConversationList.this), threadIds,
                        cursor != null && cursor.getCount() > 0,
                        ConversationList.this);
                // HAVE_LOCKED_MESSAGES_TOKEN分支中的cursor在jellybean中被及时关闭了
                if (cursor != null) {
                    cursor.close();
                }
                break;
     
            default:
                Log.e(TAG, "onQueryComplete called with unknown token " + token);
            }
        }
    }
     
     
    @Override
    protected void onStop() {
        super.onStop();
        mListAdapter.changeCursor(null);
    }
     

     StrictMode

         http://developer.android.com/reference/android/os/StrictMode.html   

         Android 2.3起,新增加了一个新的类,叫StrictMode(android.os.StrictMode)。这个类可以用来帮助开发者改进他们编写的应用,并且提供了各种的策略,这些策略能随时检查和报告开发者开发应用中存在的问题,比如可以监视那些本不应该在主线程中完成的工作或者其他的一些不规范和不好的代码。

         主要检测了读写操作,访问网络,数据库读写等耗时的操作并将其以log或者dialog等形式打印出来。分析这些日志,我们可以尽快找出程序运行缓慢的原因进而优化代码,避免ANR(Application Not Responding)窗口的出现。

      StrictMode有多种不同的策略,每一种策略又有不同的规则,当开发者违背某个规则时,每个策略都有不同的方法去显示提醒用户。

    Example code to enable from early in your ApplicationActivity, or other application component's onCreate() method:

     public void onCreate() {
         if (DEVELOPER_MODE) {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectDiskReads()
                     .detectDiskWrites()
                     .detectNetwork()   // or .detectAll() for all detectable problems
                     .penaltyLog()
                     .build());
             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                     .detectLeakedSqlLiteObjects()
                     .detectLeakedClosableObjects()
                     .penaltyLog()
                     .penaltyDeath()
                     .build());
         }
         super.onCreate();
     }

    参考链接:

    http://www.cnblogs.com/qianxudetianxia/archive/2012/11/19/2757376.html 

  • 相关阅读:
    case when then 根据不同条件 查询不同的数据 相当于 if-else if-else
    完美的拼接sql语句,中间可以加字符等东西,
    C++抽象类
    C #引用NuGet程序包MySQLData问题
    win10磁盘100%占用解决方法
    C# ASP.NetCore 检测到包降级
    VS 命令“npm install”已退出的问题
    序列化和反序列化含义
    数据库MySQL忘记本地密码
    MongoDB授予权限
  • 原文地址:https://www.cnblogs.com/willyan/p/2980459.html
Copyright © 2011-2022 走看看