web-dev-qa-db-fra.com

Erreur du domaine "accès à partir d'un thread incorrect" lors de l'utilisation de code partagé entre IntentService et AsyncTask (Android)

J'ai un code qui télécharge le JSON d'un objet "Actuel". Mais ce même code doit être appelé par un service IntentService chaque fois qu'une alarme est déclenchée (lorsque l'application n'exécute aucune interface utilisateur), ainsi que par une AsyncTask pendant l'exécution de l'application.

Cependant, j'ai eu une erreur en disant Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created. Cependant, je ne comprends pas comment ou pourquoi cette trace de pile est sur un autre thread. 

J'ai pu supprimer cette erreur en copiant tout le code partagé et en le collant directement dans la méthode onHandleIntent de DownloadDealService, mais elle est très bâclée et je recherche une meilleure solution qui n'exige pas de duplication de code.

Comment puis-je me débarrasser de cette erreur sans dupliquer le code? Merci.

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

Trace de la pile:

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)

J'ai un AsyncTask qui appelle certaines de ces méthodes Utils également:

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

[MISE À JOUR] sur la base des informations supplémentaires

RealmDatabase.getInstance () renvoyait l'instance de royaume créée sur le thread principal. Et cette instance a été utilisée sur le thread IntentService. Ce qui a conduit à l'accident.

Les instances de domaine ne peuvent être utilisées sur aucun autre thread, à l'exception de celui sur lequel elles ont été créées.


Vous ne pouvez pas transmettre d'objets Realm entre les threads. Ce que vous pouvez faire est de passer un identifiant unique de l'objet (c'est-à-dire @PrimaryKey), puis d'extraire l'objet par son identifiant sur un autre thread. Comme ceci: realm.where (YourRealmModel.class) .equalTo ("primaryKeyVariable", id) .findFirst ().

Pour plus de détails, consultez la documentation officielle et l'exemple de Realm:

Si vous utilisez le royaume dans IntentService, vous utiliserez un nouveau royaume. Par exemple 

Realm.getInstance(RealmConfiguration config) 

J'ai utilisé le royaume dans IntentService 

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

Le problème est que vous appelez des méthodes de différents threads, vous devez utiliser le même thread et créer votre propre HandlerThread.

0
josedlujan