2011-05-06 199 views
17

環境(Linux的/ Eclipse的開發對Xoom的平板電腦上運行蜂窩3.0.1)Android的錯誤:java.lang.IllegalStateException:嘗試重新查詢一個已經關閉的遊標

在我的應用我使用的相機(startIntentForResult( )) 拍照。拍完照片後,我得到了onActivityResult()回調,並能夠使用通過「拍照」意圖傳遞的Uri加載Bitmap。在這一點上我的活動重新開始,我在嘗試將圖像加載到圖片庫的錯誤:

FATAL EXCEPTION: main 
ERROR/AndroidRuntime(4148): java.lang.RuntimeException: Unable to resume activity {...}: 
java.lang.IllegalStateException: trying to requery an already closed cursor 
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2243) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1019) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:126) 
    at android.app.ActivityThread.main(ActivityThread.java:3997) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:491) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) 
    at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.IllegalStateException: trying to requery an already closed cursor 
    at android.app.Activity.performRestart(Activity.java:4337) 
    at android.app.Activity.performResume(Activity.java:4360) 
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2205) 
    ... 10 more 

我使用的唯一光標邏輯是圖像拍攝後我轉換的URI文件使用以下邏輯

String [] projection = { 
    MediaStore.Images.Media._ID, 
    MediaStore.Images.ImageColumns.ORIENTATION, 
    MediaStore.Images.Media.DATA 
}; 

Cursor cursor = activity.managedQuery( 
     uri, 
     projection, // Which columns to return 
     null,  // WHERE clause; which rows to return (all rows) 
     null,  // WHERE clause selection arguments (none) 
     null);  // Order-by clause (ascending by name) 

int fileColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 
if (cursor.moveToFirst()) { 
    return new File(cursor.getString(fileColumnIndex)); 
} 
return null; 

任何想法我做錯了什麼?

回答

22

看起來像在Honeycomb API中棄用了managedQuery()調用。

文件的managedQuery()讀取:

This method is deprecated. 
Use CursorLoader instead. 

Wrapper around query(android.net.Uri, String[], String, String[], String) 
that the resulting Cursor to call startManagingCursor(Cursor) so that the 
activity will manage its lifecycle for you. **If you are targeting HONEYCOMB 
or later, consider instead using LoaderManager instead, available via 
getLoaderManager()**. 

此外,我注意到,我打電話cursor.close(),我想查詢後是一個沒有沒有。同樣發現這個really helpful link。經過一番閱讀後,我想出了這種似乎可行的改變。

// causes problem with the cursor in Honeycomb 
Cursor cursor = activity.managedQuery( 
     uri, 
     projection, // Which columns to return 
     null,  // WHERE clause; which rows to return (all rows) 
     null,  // WHERE clause selection arguments (none) 
     null);  // Order-by clause (ascending by name) 

// ------------------------------------------------------------------- 

// works in Honeycomb 
String selection = null; 
String[] selectionArgs = null; 
String sortOrder = null; 

CursorLoader cursorLoader = new CursorLoader(
     activity, 
     uri, 
     projection, 
     selection, 
     selectionArgs, 
     sortOrder); 

Cursor cursor = cursorLoader.loadInBackground(); 
+0

你是專門針對蜂窩。剛剛發現,由於startManagingCursor(),我的Eclair +應用程序在Honeycomb中被破解。我的應用程序針對的是手機,但是當冰淇淋三明治出現時,並不期待憤怒的客戶。 – Tenfour04 2011-05-13 22:05:47

+0

是的,我專門針對蜂窩(片劑)。 – 2011-05-18 20:58:16

+0

@ cyber-monk嗨 - 我有問題。我無法實現這個,因爲我的代碼無法識別CursorLoader。我正確導入它,但它不能識別導入。我該如何解決? – Mxyk 2012-03-02 13:51:05

5

FIX:使用context.getContentResolver().query代替activity.managedQuery

Cursor cursor = null; 
try { 
    cursor = context.getContentResolver().query(uri, PROJECTION, null, null, null); 
} catch(Exception e) { 
    e.printStackTrace(); 
} 
return cursor; 
+0

我正面臨Android操作系統版本3.0及以上的類似問題,我針對智能手機和平板電腦的應用程序。我嘗試了上述建議的解決方案,他們似乎並沒有解決這個問題。如果每個人都有解決此問題的其他方法,請發佈。 – Rise 2011-11-17 12:44:43

+0

@Rupesh。我想你現在已經解決了你的問題,但是既然你問了,我已經在一個新的答案中發佈了我的解決方案。希望其他人會從中受益。 – 2012-02-04 11:17:16

7

爲了記錄在案,這裏是我如何解決了這個問題在我的代碼(運行在Android 1.6及更高版本):在我的情況的問題是,我是無意中通過調用CursorAdapter.changeCursor關閉管理光標()。調用Activity.stopManagingCursor()在適配器的光標改變光標之前解決了這個問題:

// changeCursor() will close current one for us: we must stop managing it first. 
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); // *** adding these lines 
stopManagingCursor(currentCursor);           // *** solved the problem 
Cursor c = db.fetchItems(selectedDate); 
startManagingCursor(c); 
((SimpleCursorAdapter)getListAdapter()).changeCursor(c); 
3

我創建了這個問題,在這裏,因爲我無法在最後的答案(因爲某些原因禁用評論)發表評論。我認爲在這個問題上打開一個新線程只會使事情複雜化。

我得到的應用程序崩潰,當我從活動A活動B去,然後回到活動A。這不會一直髮生 - 只是有時候,我很難找到確切發生的地方。全部發生在同一臺設備上(Nexus S),但我不認爲這是設備問題。

關於@Martin Stine的回答,我有幾個問題。

  • 在關於changeCursor(c);的文檔中提到:「將底層遊標更改爲新的遊標,如果存在已存在的遊標,它將被關閉」。那麼爲什麼我要stopManagingCursor(currentCursor); - 是不是多餘的
  • 當我使用@Martin Stine提供的代碼時,我得到一個空指針異常。原因是在應用程序((SimpleCursorAdapter)getListAdapter())的第一個「運行」中將評估爲NULL,因爲尚未創建遊標。當然,我可以檢查是否我沒有得到一個null,然後嘗試停止管理光標,但最後我決定放置我的`stopManagingCursor(currentCursor);在此活動的onPause()方法中。我認爲這樣我肯定會有一個光標停止管理,我應該在我離開活動到另一個之前做。問題 - 我在我的活動中使用了幾個遊標(一個填充了EditText字段的文本,另一個填充了列表視圖的文本)我猜並非所有這些都與ListAdapter遊標有關 -
    • 我該如何知道哪一個停止管理?如果我有3個不同的列表視圖?
    • 我應該在onPause()期間全部關閉嗎?
    • 如何獲取我打開的所有遊標的列表?

這麼多的問題...希望有人可以幫助。

當我到達onPause()時,我確實有一個遊標停止管理,但我還沒有確定這是否解決了問題,因爲此錯誤偶爾會出現。

非常感謝!


一些調查後:

我發現了一些有趣的是,可能會給回答這個問題的「神祕」的一面:

活動A使用兩個光標:一個填補一個EditText領域。另一個是填充ListView。

當從活動A移動到活動B並返回時,必須再次填充活動A中的字段+ ListView。看起來EditText字段永遠不會有問題。我無法找到獲取EditText字段當前光標的方法(如Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();),原因告訴我EditText字段不會保留它。另一方面,ListView將從上次(從活動A→活動B之前)「記住」它的光標。此外,這是很奇怪的,在Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();將活動B後不同的ID - >活動A而這一切WITHOUT我曾經呼籲

Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); 
stopManagingCursor(currentCursor); 

我想在某些情況下,系統需求時,爲了釋放資源,遊標將被殺死,當Activity B - > Activity A時,系統仍會嘗試使用這個舊的死遊標,這將導致異常。而在其他情況下,系統會產生一個仍然存在的新遊標,因此不會發生異常。這也許可以解釋爲什麼有時只顯示這些。我想這是很難調試,因爲在運行或調試應用程序時,由於應用程序速度的差異。調試時,需要更多時間,因此可能會讓系統有時間想出新的光標,反之亦然。

在我的理解,這使得的

Cursor currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); 
stopManagingCursor(currentCursor); 

使用所推薦的@馬丁斯坦一個必須在某些情況下,而在其他冗餘:如果我回到方法和系統試圖要使用死光標,必須創建一個新的光標並將其替換到ListAdapter中,否則我會生氣地碰到一個應用程序崩潰的應用程序用戶。在另一種情況下,系統會發現自己是一個新的光標 - 上面的行是多餘的,因爲它們使一個好的光標失效並創建一個新的光標。

我想爲了防止這種冗餘,我需要這樣的:

ListAdapter currentListAdapter = getListAdapter(); 
Cursor currentCursor = null; 
Cursor c = null; 

//prevent Exception in case the ListAdapter doesn't exist yet 
if(currentListAdapter != null) 
    { 
     currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); 

        //make sure cursor is really dead to prevent redundancy 
        if(currentCursor != null) 
        { 
         stopManagingCursor(currentCursor); 

         c = db.fetchItems(selectedDate); 

         ((SimpleCursorAdapter)getListAdapter()).changeCursor(c); 
        } 
        else 
        { 
         c = db.fetchItems(selectedDate); 

        } 
    } 
      else 
      { 
       c = db.fetchItems(selectedDate); 

      } 

startManagingCursor(c); 

我很想聽聽你想想這個!

0

這個問題困擾了我很長一段時間,我終於想出了一個簡單的解決方案,就像所有Android版本的魅力一樣。首先,不要使用startManagingCursor(),因爲它在任何情況下顯然都是bug且不推薦使用。其次,在完成之後儘快關閉光標。我使用try和finally來確保遊標在任何情況下都關閉。如果你的方法必須返回一個Cursor,那麼調用例程負責儘快關閉它。

我曾經在一個Activity的生命週期中讓遊標保持打開狀態,但是我已經放棄了這個事務性方法。現在我的應用程序非常穩定,並且在切換活動時即使訪問相同的數據庫也不會遇到「Android錯誤:java.lang.IllegalStateException:嘗試重新查找已關閉的遊標」。

static public Boolean musicReferencedByOtherFlash(NotesDB db, long rowIdImage) 
    { 
     Cursor dt = null; 
     try 
     { 
     dt = db.getNotesWithMusic(rowIdImage); 
     if ( (dt != null) 
      && (dt.getCount() > 1)) 
      return true; 
     } 
     finally 
     { 
     if (dt != null) 
      dt.close(); 
     } 
     return false; 
    } 
2

就在光標塊的結尾處添加以下代碼..

try { 
       Cursor c = db.displayName(number); 

       startManagingCursor(c); 
       if (!c.moveToFirst()) { 
        if (logname == null) 
         logname = "Unknown"; 
        System.out.println("Null " + logname); 
       } else { 
        logname = c.getString(c 
          .getColumnIndex(DataBaseHandler.KEY_NAME)); 
        logdp = c.getBlob(c 
          .getColumnIndex(DataBaseHandler.KEY_IMAGE)); 
        // tvphoneno_oncall.setText(logname); 
        System.out.println("Move name " + logname); 
        System.out.println("Move number " + number); 
        System.out.println("Move dp " + logdp); 
       } 

       stopManagingCursor(c); 
      } 
+1

stopManagingCursor(c);最後爲我工作!簡單! :) – 2014-04-20 16:42:39

相關問題