16

Ho un codice che scarica un JSON dell'oggetto" Corrente ". Ma questo stesso codice deve essere chiamato da un IntentService ogni volta che un allarme si spegne (quando l'app non sta eseguendo alcuna interfaccia utente), e anche da un AsyncTask mentre l'app è in esecuzione.Errore `accesso da thread errato` quando si utilizza il codice condiviso tra IntentService e AsyncTask (Android)

Tuttavia, ho ricevuto un errore che diceva Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created. Tuttavia, non capisco come o perché questa traccia dello stack sia stata inserita in un thread diverso.

Sono stato in grado di eliminare l'errore copiando tutto il codice condiviso e incollandolo direttamente nel metodo onHandleIntent di DownloadDealService, ma è molto sciatto e sto cercando una soluzione migliore che non richieda la duplicazione del codice.

Come posso eliminare questo errore, senza duplicare il codice? Grazie.

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

traccia stack:

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) 

ho un AsyncTask che chiama alcuni di questi metodi Utils anche:

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

risposta

18

[AGGIORNATO] sulla base delle informazioni aggiuntive

RealmDatabase.getInstance() stava tornando l'istanza regno creato sul thread principale. E questa istanza è stata utilizzata nella discussione IntentService. Che portano allo schianto.

Le istanze del realm non possono essere utilizzate su nessun altro thread tranne quello su cui sono stati creati.


Non è possibile passare oggetti Realm tra i thread. Quello che puoi fare è passare un identificatore univoco dell'oggetto (ad esempio @PrimaryKey), e quindi recuperare l'oggetto con il suo 'id su un altro thread. In questo modo: realm.where (YourRealmModel.class) .equalTo ("primaryKeyVariable", id) .findFirst().

Per ulteriori dettagli check-out ufficiale di documentazione e l'esempio di Realm:

+0

Puoi guardare il mio codice e dirmi dove ho passato qualcosa tra i fili? Non capisco. Onestamente pensavo di essere sullo stesso thread. Per quanto ne so, ho solo condiviso i metodi tra i thread, non ho passato alcun oggetto realm tra i thread. –

+0

È piuttosto difficile capire cosa va dal tuo esempio di codice.Ma suppongo che tu ottenga l'oggetto risultato in doInBackround dell'AsyncTask (che viene eseguito sul thread worker) e poi lo passi come risultato a onPostExecute() (che viene eseguito sul thread principale). E poi provando ad accedervi da lì sul metodo remainder(), che fa esplodere la tua app. –

+0

È possibile cercare nella traccia stack fornita per visualizzare il percorso di esecuzione. IntentService 'DownloadDealService' chiama alcuni metodi statici nella mia classe' Util.java'. –

0

Il problema è che si sta chiamando i metodi da differenti thread, avete per usare lo stesso thread, crea il tuo HandlerThread.

0

Se si utilizza il Reame in IntentService, si utilizzerà nuovo Regno. per esempio

Realm.getInstance(RealmConfiguration config) 

Ho usato il Realm in IntentService

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