4

我有正在由一個CursorAdapterLoaderManager得到了Cursor的活動填充Fragment個系列ListView對象。據我所知,所有數據庫和Cursor關閉行動完全由LoaderManagerContentProvider處理,所以在任何代碼我都不打電話.close()任何東西。IllegalStateException異常「試圖重新打開已關閉的對象」在SimpleCursorAdapter從ContentProvider的

但是,有時候,我得到這個異常:

02-19 11:07:12.308 E/AndroidRuntime(18777): java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT * FROM privileges WHERE uuid!=?) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:33) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:82) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:147) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:178) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162) 
02-19 11:07:12.308 E/AndroidRuntime(18777): at android.widget.CursorAdapter.getView(CursorAdapter.java:241) 

我把一些日誌代碼到我的CursorAdapter告訴我,當getView(...)getItem(...)getItemId(...)被調用,它好像這對正在發生的事情第一個getView(...)對於一個給定的適配器,在很多getView(...)之後用於另一個適配器。它也發生在用戶在應用程序周圍導航很多之後。

這使我懷疑,如果Cursor爲適配器被保留在CursorAdapter,而是由ContentProviderLoader被錯誤地關閉。這可能嗎?我應該根據app/activity/fragment生命週期事件對CursorAdapter做任何管家嗎?

ContentProvider查詢方法:

class MyContentProvider extends ContentProvider { 
//... 

    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, 
      String[] selectionArgs, String sortOrder) { 
     SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 
     Cursor query = db.query(getTableName(uri), projection, selection, selectionArgs, null, null, sortOrder); 
     query.setNotificationUri(getContext().getContentResolver(), uri); 
     return query; 
    } 

//... 
} 

典型LoaderCallbacks

LoaderCallbacks<Cursor> mCallbacks = new LoaderCallbacks<Cursor>() { 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     mArticleAdapter.swapCursor(null); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 
     if(cursor.isClosed()) { 
      Log.d(TAG, "CURSOR RETURNED CLOSED"); 
      Activity activity = getActivity(); 
      if(activity!=null) { 
       activity.getLoaderManager().restartLoader(mFragmentId, null, mCallbacks); 
      } 
      return; 
     } 
     mArticleAdapter.swapCursor(cursor); 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     triggerArticleFeed(); 
     CursorLoader cursorLoader = null; 

     if(id == mFragmentId) { 
      cursorLoader = new CursorLoader(getActivity(), 
              MyContentProvider.ARTICLES_URI, 
              null, 
              ArticlesContentHelper.ARTICLES_WHERE, 
              ArticlesContentHelper.ARTICLES_WHEREARGS, 
              null); 
     } 
     return(cursorLoader); 
    } 
}; 

CursorAdapter構造:

public ArticlesCursorAdapter(Context context, Cursor c) { 
    super(context, c, 0); 
    mImageloader = new ImageLoader(context); 
} 

我讀過這個問題,但遺憾的是它沒有得到答案我的問題,因爲它只是建議usi ng ContentProvider,我就是。

IllegalStateException: attempt to re-open an already-closed object. SimpleCursorAdapter problems

剛剛顯露出來

重要新信息我在沒有使用Loader S和無法正常管理其Cursor S中的項目在其他地方發現了一些其他的代碼。我剛剛切換了這段代碼以使用與上面相同的模式;然而,如果解決了這個問題,那麼在某個項目的某個部分會出現一個非託管的Cursor可能會在其他地方殺死一個正確管理的項目。

堅持。

觀察新信息

沒有解決它。

NEW IDEA

@Override 
onDestroyView() { 
    getActivity().getLoaderManager().destroyLoader(mFragmentId); 
    //any other destroy-time code 
    super.onDestroyView() 
} 

即可能是的,我應該將在CursorAdapter(或者說CursorLoader與生命週期事件系)做家政服務。

觀察NEW IDEA

都能跟得上的。

上一頁IDEA

原來工作,一旦我在一個小調整加入!不過,這很複雜,我應該重寫整個問題。

+0

我們展示一些代碼...'ContentProvider.query'和'OnLoadCompleteListener.onLoadComplete' – Selvin 2013-02-19 11:46:52

+0

OK詳細信息...在'onLoadComplete'你應該簡單地使用'Adapter.swapCursor(newCursor);'.. 。next ...不要在適配器中存儲Cursor實例,只需在適配器內部使用'getCursor()'...所以在光標交換後,您將獲得光標的新實例... – Selvin 2013-02-19 11:53:50

+0

@Selvin這就是我所做的,我只直接使用傳遞給'bindView(...)'的遊標。當光標移動到「CursorAdapter」代碼的位置(我沒有重寫)時,實際上會發生崩潰。 – 2013-02-19 11:59:11

回答

0

您是否更新了數據集?這可能是因爲光標已被重新加載,由於通知的內容解析變化的情況:

getContentResolver().notifyChange(URI, null); 

如果你設置了一個通知URI,這將觸發當前光標關閉和一個新的光標由光標加載器返回。然後,您可以抓住新的光標,如果你已經註冊了onLoadCompleteListener:

mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() { 
    @Override 
    public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) { 
     // Set your listview's CursorAdapter 
    } 
}); 
+0

我正在使用這個,並且我已經設置了'Cursor'來接收該URI上的通知。我認爲'CursorAdapter'應該自動處理這個呢?我已經在實現'LoaderCallbacks'並且在那裏調用'CursorAdapter#swapCursor(...)'。 – 2013-02-19 12:01:13

+0

我認爲這可能是相關的,實際上:我打電話給'notifyChange(...)'。然而,你通常會期望'Loader'在LoaderCallbacks中命中'onLoadFinished(...)',如果不是''OnLoadCompleteListener'中的命令'onLoadComplete(...)'?這似乎有點像重複。請讓我知道如果我需要這樣做。 – 2013-02-19 17:10:04

+0

我產生了一個新的問題: http://stackoverflow.com/questions/14963524/is-it-necess- to-implement-both-loadercallbacks-and-onloadcompletelistener-to – 2013-02-19 17:25:25

0

你可以嘗試的路徑,而不是空光標到適配器構造函數。然後將適配器中的SwapCursor(Cursor c)函數移動到那裏,並將光標數據的初始化移動到數據加載器的OnLoadFinished(Loader loader,Cursor data)方法中。

enter code here 
    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
    // ... building your query here 
     mSimpleCursorAdapter = new mSimpleCursorAdapter(getActivity().getApplicationContext(), 
      layout, null, from, to, flags); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     contentAdapter.swapCursor(data); 
    }