18

環境(HoneyComb3.0.1を実行しているXoomTablet用のLinux/ Eclipse Dev)

私のアプリでは、カメラ(startIntentForResult())を使用して写真を撮っています。写真を撮った後、onActivityResult()コールバックを取得し、「写真を撮る」インテントを介して渡されたURIを使用してビットマップをロードできます。その時点でアクティビティが再開され、画像をギャラリーにリロードしようとするとエラーが発生します。

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;

私が間違っていることについて何か考えはありますか?

4

6 に答える 6

23

ManagedQuery()呼び出しはHoneycombAPIで非推奨になっているようです。

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()を呼び出していることに気づきました。この本当に役立つリンクも見つかりました。いくつか読んだ後、私はうまくいくように見えるこの変更を思いついた。

// 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();
于 2011-05-06T18:40:54.830 に答える
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);
于 2012-02-04T11:28:54.023 に答える
5

修正: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;
于 2011-09-19T10:17:24.107 に答える
3

最後の回答にコメントできなかったため、ここでこの質問を作成しました(何らかの理由でコメントが無効になっています)。これについて新しいスレッドを開くと、事態は複雑になるだけだと思いました。

アクティビティAからアクティビティBに移動してから、アクティビティAに戻ると、アプリケーションがクラッシュします。これは常に発生するわけではありません。たまにしか発生せず、これが発生する場所を正確に見つけるのに苦労しています。すべて同じデバイス(Nexus S)で発生しますが、これはデバイスの問題ではないと思います。

@MartinStineの回答に関していくつか質問があります。

  • ドキュメントには、changeCursor(c);「下にあるカーソルを新しいカーソルに変更します。既存のカーソルがある場合は閉じられます」と記載されています。では、なぜ私はそうしなければならないのですかstopManagingCursor(currentCursor);それは冗長ではありませんか?
  • @Martin Stineが提供するコードを使用すると、nullポインター例外が発生します。その理由は、((SimpleCursorAdapter)getListAdapter())カーソルがまだ作成されていないため、アプリケーションの最初の「実行」でNULLと評価されるためです。確かに、nullを取得しないかどうかを確認してから、カーソルを管理するために停止してみますが、最終的には `stopManagingCursor(currentCursor);を配置することにしました。このアクティビティのonPause()メソッドで。このようにすると、管理を停止するカーソルが確実にあり、アクティビティを別のアクティビティに任せる直前に実行する必要があると思いました。問題-アクティビティで複数のカーソル(1つはEditTextフィールドのテキストを埋めるため、もう1つはリストビュー用)を使用していますが、すべてがListAdapterカーソルに関連しているわけではないと思います-
    • 管理を停止するものをどのように知ることができますか?3つの異なるリストビューがある場合はどうなりますか?
    • 中にすべてを閉じる必要がありますonPause()か?
    • 開いているすべてのカーソルのリストを取得するにはどうすればよいですか?

非常に多くの質問...誰かが助けてくれることを願っています。

到達したときonPause()、管理を停止するカーソルがありますが、このエラーが散発的に表示されるため、これで問題が解決するかどうかはまだわかりません。

どうもありがとう!


いくつかの調査の後:

私はこの問題の「神秘的な」側面に答えを与えるかもしれない何か面白いものを見つけました:

アクティビティAは2つのカーソルを使用します。1つはEditTextフィールドに入力します。もう1つは、ListViewにデータを入力することです。

アクティビティAからアクティビティBに移動して戻ってくるときは、アクティビティAのフィールド+ListViewを再度入力する必要があります。EditTextフィールドで問題が発生することはないようです。EditTextフィールドの現在のカーソルを取得する方法が見つかりませんでした(のようにCursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();)。理由は、EditTextフィールドがそれを保持しないことを示しています。一方、ListViewは、前回から(アクティビティA->アクティビティBの前から)カーソルを「記憶」します。さらに、これは奇妙なことですが、Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();アクティビティB->アクティビティAの後に異なるIDがあり、これはすべてが電話することなく行われます

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

場合によっては、システムがリソースを解放する必要があるときにカーソルが強制終了され、アクティビティB->アクティビティAのときに、システムはこの古いデッドカーソルを使用しようとするため、例外が発生することがあります。また、他の場合には、システムはまだ生きている新しいカーソルを考え出すので、例外は発生しません。これは、これがたまにしか表示されない理由を説明している可能性があります。アプリケーションの実行時またはデバッグ時のアプリケーション速度の違いにより、これをデバッグするのは難しいと思います。デバッグする場合、時間がかかるため、システムが新しいカーソルを見つけるのに時間がかかる場合があります。その逆の場合もあります。

私の理解では、これは

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

@Martin Stineが推奨するように、場合によっては必須であり、その他の場合は冗長です。メソッドに戻り、システムがデッドカーソルを使用しようとしている場合は、新しいカーソルを作成して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);

あなたがこれについてどう思うか聞いてみたいです!

于 2012-06-01T07:01:42.003 に答える
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);
            } 
于 2013-07-09T12:16:02.117 に答える
0

この問題は長い間私を悩ませていました、そして私はついにAndroidのすべてのバージョンで魅力のように働く簡単な解決策を思いつきました。まず、startManagingCursor()は明らかにバグがあり、いずれの場合も非推奨であるため、使用しないでください。次に、カーソルを使い終わったら、できるだけ早くカーソルを閉じます。私はtryとfinallyを使用して、すべての状況でカーソルが閉じられるようにします。メソッドがカーソルを返す必要がある場合、呼び出し元のルーチンはできるだけ早くカーソルを閉じる責任があります。

以前はアクティビティの存続期間中はカーソルを開いたままにしていましたが、このトランザクションアプローチではそれを放棄しました。これで、私のアプリは非常に安定し、同じデータベースにアクセスしていても、アクティビティを切り替えるときに「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;
   }
于 2012-10-23T15:15:22.820 に答える