新手在使用数据库的时候,很容易忘记关闭Cursor,造成严重内存泄漏。有什么好的方法解决这个问题呢?除了自己管理Cursor,有什么办法把Cursor交给系统管理,而不需要自己主动close呢?manageQuery可以为你维护这个cursor。在你退出activity时为你自动关闭。
The method managedQuery(Uri, String[], String, String[], String) from the type Activity is deprecated。
android官方文档推荐用CursorLoader类来取代。
我们先来分析一下manageQuery是如何管理cursror的
private static final class ManagedCursor { ManagedCursor(Cursor cursor) { mCursor = cursor; mReleased = false; mUpdated = false; } private final Cursor mCursor; private boolean mReleased; private boolean mUpdated; } private final ArrayList<ManagedCursor> mManagedCursors = new ArrayList<ManagedCursor>();
public final Cursor managedQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder); if (c != null) { startManagingCursor(c); } return c; }
public void startManagingCursor(Cursor c) { synchronized (mManagedCursors) { mManagedCursors.add(new ManagedCursor(c)); } }
protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); }
看来这几个方法就已经非常明确了。将cursor封装之后放到一个ArrayList中,在Activity的Destroy方法中关闭cursor,Activity正常销毁一定会走OnDestroy()方法的,这样可以保证cursor会被关闭。
LoaderManager方法有下面几个方面更胜一筹:
1. startManagingCursor管理Cursors,而LoaderManager管理Loader<D>对象。Loader<D>是一个模板类,D是一个包含了需要载入数据的类。也就是说,数据源并不必须是Cursor,它可以是List、JSONArray。。。任何类。LoaderManager与它包含的数据内容解耦了,所以更加灵活。
2. 调用startManagingCursor会导致Activity在已经加入管理的Cursor上调用requery()方法。在第一篇文章中提到过,在UI线程执行requery()方法代价非常高。相反的是,Loader<D>的子类会异步载入(译者注:另外再开一个线程,就叫异步了。)数据,所以使用LoaderManager永远不会产生阻塞UI线程的情况。
3. startManagingCursor在配置变化(例如横竖屏切换)时,并不会保持Cursor的状态。麻烦的是,每次Activity由于配置变化(比如,简单的横竖屏切换),都会导致Cursor下回并且被重新查询。LoaderManager就智能多了,当配置变化时,它会保持它里面的Loader们的状态,所以并不需要重新查询数据。
4. LoaderManager提供无缝的数据监测。任何时候,当Loader的数据源改变,LoaderManager将会从对应的Loader上面接收到一个新的同步载入数据,并将更新数据返回给客户端(注意:仅当Loader被正确实现,LoaderManager才会接收到这些数据变化通知。我们将会在该系列的第三篇文章讨论自定义Loaders的实现)。
CursorLoader使用LoaderManager管理Cursor
使用方法如下
LoaderCallbacks<Cursor> callback= new LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // 返回自己需要的CursorLoader return null ; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // 使用swapCursor()方法,以使旧的游标不被关闭. simpleCursorAdapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader<Cursor> loader) { 、 // 一般写这样写 simpleCursorAdapter.swapCursor(null); } }; getLoaderManager().initLoader(0, null, callback);
需要注意的是当使用initLoader()时,如果指定ID的装载器已经存在,则它使用这个装载器.如果不存在呢,它将创建一个新的.但是有时你却是想丢弃旧的然后开始新的数据,要使用restartLoader()方法。
欢迎扫描二维码,关注公众号