web-dev-qa-db-ger.com

Realm-Zugriff vom falschen Thread bei der Verwendung von gemeinsamem Code zwischen IntentService und AsyncTask (Android)

Ich habe etwas Code, der die JSON eines "aktuellen" Objekts herunterlädt. Derselbe Code muss jedoch von einem IntentService aufgerufen werden, wenn ein Alarm ausgelöst wird (wenn die App keine Benutzeroberfläche ausführt) und auch von einer AsyncTask, während die App ausgeführt wird.

Ich habe jedoch eine Fehlermeldung erhalten, die Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created. sagt. Ich verstehe jedoch nicht, wie oder warum diese Stack-Ablaufverfolgung auf einen anderen Thread kam. 

Ich konnte den Fehler beseitigen, indem ich den gesamten gemeinsam genutzten Code kopierte und ihn direkt in die onHandleIntent-Methode von DownloadDealService einfügte, aber er ist sehr schlampig und ich suche nach einer besseren Lösung, die keinen Code duplizieren muss.

Wie kann ich diesen Fehler beheben, ohne Code zu duplizieren? Vielen Dank.

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;
    }
}

Stack-Trace:

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)

Ich habe eine AsyncTask, die auch einige dieser Utils-Methoden aufruft:

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

[AKTUALISIERT] basierend auf den zusätzlichen Informationen

RealmDatabase.getInstance () gab die Realm-Instanz zurück, die im Hauptthread erstellt wurde. Und diese Instanz wurde im IntentService-Thread verwendet. Was zum Absturz führte.

Bereichsinstanzen können in keinem anderen Thread als dem verwendet werden, in dem sie erstellt wurden.


Sie können keine Realm-Objekte zwischen den Threads übergeben. Sie können einen eindeutigen Bezeichner des Objekts übergeben (d. H. @PrimaryKey) und das Objekt dann anhand seiner 'id in einem anderen Thread abrufen. Wie folgt: realm.where (YourRealmModel.class) .equalTo ("primaryKeyVariable", id) .findFirst ().

Weitere Informationen finden Sie in der offiziellen Dokumentation und dem Beispiel von Realm:

Wenn Sie das Realm in IntentService verwenden, verwenden Sie beispielsweise das neue Realm 

Realm.getInstance(RealmConfiguration config) 

Ich habe das Realm in IntentService verwendet 

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

Das Problem ist, dass Sie Methoden aus verschiedenen Threads aufrufen. Sie müssen den gleichen Thread verwenden und Ihr eigenes HandlerThread erstellen.

0
josedlujan