16

Ich habe Code, der ein JSON eines aktuellen Objekts herunterlädt. Aber der gleiche Code muss 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.Realm `Zugriff von falschem Thread` Fehler bei der Verwendung von gemeinsam genutztem Code zwischen IntentService und AsyncTask (Android)

Allerdings habe ich einen Fehler, Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created. zu sagen, aber ich verstehe nicht, wie oder warum diese Stack-Trace auf einem anderen Thread bekam.

Ich konnte den Fehler loswerden, indem ich den gesamten Code kopiere und direkt in die Methode onHandleIntent von DownloadDealService stecke, aber es ist sehr schlampig und ich suche nach einer besseren Lösung, die keinen Code duplizieren muss.

Wie kann ich diesen Fehler loswerden, 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; 
    } 
} 

Stapelüberwachung:

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

Antwort

18

[updated] auf der Grundlage der zusätzlichen Informationen

RealmDatabase.getInstance() wurde die Realm-Instanz zurückkehr auf dem Haupt-Thread erstellt. Und diese Instanz wurde im Thread des IntentService verwendet. Was zum Absturz führte.

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


Sie können keine Realm-Objekte zwischen den Threads übergeben. Sie können einen eindeutigen Bezeichner des Objekts (z. B. @PrimaryKey) übergeben und dann das Objekt mit der ID eines anderen Threads abrufen. Beispiel: realm.where (YourRealmModel.class) .equalTo ("primaryKeyVariable", id) .findFirst().

Für weitere Informationen überprüfen Realm offizielle Dokumentation und Beispiel:

+0

Kannst du mir meinen Code ansehen und mir sagen, wo ich irgendetwas zwischen Threads weitergegeben habe? Ich verstehe nicht. Ich dachte ehrlich, dass ich im selben Thread wäre. Soweit ich weiß, habe ich nur Methoden zwischen Threads geteilt, keine Realm-Objekte zwischen Threads übergeben. –

+0

Es ist ziemlich schwer zu verstehen, was von Ihrem Codebeispiel ausgeht.Aber ich nehme an, dass Sie das Ergebnisobjekt in doInBackround der AsyncTask (die auf dem Arbeitsthread ausgeführt wird) erhalten und dann als Ergebnis an onPostExecute() übergeben (was im Hauptthread ausgeführt wird). Und dann versuchen Sie es von dort auf Rest() -Methode, die Ihre App sprengt. –

+0

Sie können in der von mir bereitgestellten Stack-Trace nachsehen, um den Ausführungspfad zu sehen. Der IntentService 'DownloadDealService' ruft einige statische Methoden in meiner' Util.java' Klasse auf. –

0

Das Problem ist, Sie Methoden aus differents Threads aufrufen, Sie haben Um den gleichen Thread zu verwenden, erstellen Sie Ihren eigenen HandlerThread.

0

Wenn Sie den Realm in IntentService verwenden, werden Sie verwenden new Realm .für Beispiel

Realm.getInstance(RealmConfiguration config) 

Ich habe den Realm in IntentService verwendet

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