web-dev-qa-db-ja.com

IntentServiceとAsyncTaskの間で共有コードを使用する場合のレルム「不正なスレッドからのアクセス」エラー(Android)

「Current」オブジェクトのJSONをダウンロードするコードがあります。ただし、この同じコードは、アラームが発生したとき(アプリがUIを実行していないとき)にIntentServiceによって呼び出され、アプリの実行中にAsyncTaskによっても呼び出される必要があります。

ただし、Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.というエラーが発生しましたが、このスタックトレースが別のスレッドにどのように、またはなぜ到達したかはわかりません。

私はすべての共有コードをコピーしてDownloadDealServiceのonHandleIntentメソッドに直接貼り付けることでエラーを取り除くことができましたが、それは非常にずさんで、コードを複製する必要のないより良い解決策を探しています。

コードを複製せずにこのエラーを取り除くにはどうすればよいですか?ありがとう。

public class DownloadDealService extends IntentService
{
    ...
    @Override
    protected void onHandleIntent(Intent intent)
    {
        Current todaysCurrent = Utils.downloadTodaysCurrent(); //<--- included for background info
        String dateString = Utils.getMehHeadquartersDate(); //(omitted)
        Utils.onDownloadCurrentCompleteWithAlarm(todaysCurrent, dateString); //<------ calling this...
    }
}

public class Utils
{
    // ...other methods ommitted...

    //This method is not in the stack trace, but I included it for background information.
    public static Current downloadTodaysCurrent()
    {
        //Set up Gson object... (omitted)
        //Set up RestAdapter object... (omitted)
        //Set up MehService class... (omitted)

        //Download "Current" object from the internet.
        Current current = mehService.current(MehService.API_KEY);
        return current;
    }

    //Included for background info- this method is not in the stack trace.
    public static void onDownloadCurrentComplete(Current result, String dateString)
    {
        if(result.getVideo() == null)
        {
            Log.e("HomePage", "Current was not added on TaskComplete");
            return;
        }
        remainder(result, dateString);
    }

    public static void onDownloadCurrentCompleteWithAlarm(Current result, String dateString)
    {
        //Set alarm if download failed and exit this function... (omitted)

        remainder(result, dateString);//<------ calling this...
        Utils.sendMehNewDealNotification(App.getContext());
    }

    public static void remainder(Current result, String dateString)
    {
        Realm realm = RealmDatabase.getInstance();

        //Add "Current" to Realm
        Current current = Utils.addCurrentToRealm(result, realm); //<------ calling this...
    }

    public static Current addCurrentToRealm(Current current, Realm realm)
    {
        realm.beginTransaction(); //<---- Error is here
        Current result = realm.copyToRealmOrUpdate(current);
        realm.commitTransaction();
        return result;
    }
}

スタックトレース:

E/AndroidRuntime: FATAL EXCEPTION: IntentService[DownloadDealService]
Process: com.example.lexi.meh, PID: 13738
Java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
    at io.realm.Realm.checkIfValid(Realm.Java:191)
    at io.realm.Realm.beginTransaction(Realm.Java:1449)
    at com.example.lexi.meh.Utils.Utils.addCurrentToRealm(Utils.Java:324)
    at com.example.lexi.meh.Utils.Utils.remainder(Utils.Java:644)
    at com.example.lexi.meh.Utils.Utils.onDownloadCurrentCompleteWithAlarm(Utils.Java:635)
    at com.example.lexi.meh.Home.DownloadDealService.onHandleIntent(DownloadDealService.Java:42)
    at Android.app.IntentService$ServiceHandler.handleMessage(IntentService.Java:65)
    at Android.os.Handler.dispatchMessage(Handler.Java:102)
    at Android.os.Looper.loop(Looper.Java:136)
    at Android.os.HandlerThread.run(HandlerThread.Java:61)

これらのUtilsメソッドのいくつかを呼び出すAsyncTaskもあります:

public class DownloadAsyncTask extends AsyncTask<Void, Integer, Current>
{
    // ... (more methods ommitted)...

    protected Current doInBackground(Void... voids)
    {
        return Utils.downloadTodaysCurrent(); //<---- shared Utils method
    }
}

//Async class's callback in main activity:
public class HomePage extends AppCompatActivity implements DownloadAsyncTaskCallback, DownloadAsyncTaskGistCallback<Current, String>
{
    // ... (more methods ommitted)...

    public void onTaskComplete(Current result, String dateString)
    {
        Utils.onDownloadCurrentComplete(result, dateString);
    }
}
16
Rock Lee

[更新済み]追加情報に基づいて

RealmDatabase.getInstance()は、メインスレッドで作成されたRealmインスタンスを返していました。そして、このインスタンスはIntentServiceのスレッドで使用されました。これはクラッシュにつながります。

レルムインスタンスは、それらが作成されたスレッド以外のスレッドでは使用できません。


スレッド間でRealmオブジェクトを渡すことはできません。できることは、オブジェクトの一意の識別子(@PrimaryKey)を渡し、別のスレッドでそのidによってオブジェクトをフェッチすることです。このように:realm.where(YourRealmModel.class).equalTo( "primaryKeyVariable"、id).findFirst()。

詳細については、Realmの公式ドキュメントと例をご覧ください。

IntentServiceでレルムを使用する場合、新しいレルムを使用します。たとえば、

Realm.getInstance(RealmConfiguration config) 

IntentServiceでレルムを使用しました

@Override
protected void onHandleIntent(Intent intent) {
    Realm realm = Realm.getDefaultInstance();
    ...
    realm.close(); // important on background thread
}
1
ayac3j

問題は、異なるスレッドからメソッドを呼び出していることです。同じスレッドを使用し、独自のHandlerThreadを作成する必要があります。

0
josedlujan