標籤:
新手在使用資料庫的時候,很容易忘記關閉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()方法。
歡迎掃描二維碼,關注公眾號
android Cursor的自動管理方式