本文介绍了在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从错误线程访问"错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码可以下载当前"对象的 JSON.但是,只要警报响起(当应用未运行任何 UI 时),以及应用运行时的 AsyncTask,都需要由 IntentService 调用相同的代码.

I have some code that downloads a "Current" object's JSON. But this same code needs to be called by an IntentService whenever an alarm goes off (when the app is not running any UI), and also by an AsyncTask while the app is running.

但是,我收到一条错误消息,说从不正确的线程访问领域.Realm 对象只能在它们创建的线程上访问. 但是,我不明白这个堆栈跟踪如何或为什么在不同的线程上.

However, I got an error saying Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created. However, I do not understand how or why this stack trace got on a different thread.

通过复制所有共享代码并将其直接粘贴到 DownloadDealService 的 onHandleIntent 方法中,我能够摆脱错误,但它非常草率,我正在寻找更好的解决方案不需要重复的代码.

I was able to get rid of the error by copying all the shared code and sticking it directly into DownloadDealService's onHandleIntent method, but it is very sloppy and I'm looking for a better solution that doesn't require duplicating code.

如何在不重复代码的情况下摆脱此错误?谢谢.

How can I get rid of this error, without duplicating code? Thanks.

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)

我有一个 AsyncTask 也调用其中一些 Utils 方法:

I have an AsyncTask that calls some of those Utils methods also:

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

推荐答案

[UPDATED] 基于附加信息

[UPDATED] based on the additional info

RealmDatabase.getInstance() 返回在主线程上创建的 Realm 实例.这个实例被用在 IntentService 的线程上.导致崩溃.

RealmDatabase.getInstance() was returning the Realm instance created on the main thread. And this instance was used on the IntentService's thread. Which lead to the crash.

Realm 实例不能在任何其他线程上使用,除了创建它们的线程.

Realm instances can't be used on any other thread except the one on which they were created.

您不能在线程之间传递 Realm 对象.您可以做的是传递对象的唯一标识符(即@PrimaryKey),然后在另一个线程上通过其 id 获取对象.像这样:realm.where(YourRealmModel.class).equalTo("primaryKeyVariable", id).findFirst().

You can't pass Realm objects between the threads. What you can do is to pass a unique identifier of the object (i.e. @PrimaryKey), and then fetch the object by its' id on another thread. Like this: realm.where(YourRealmModel.class).equalTo("primaryKeyVariable", id).findFirst().

更多细节请查看 Realm 的官方文档和示例:

For more details check out Realm's official documentation and example:

这篇关于在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从错误线程访问"错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-19 05:40