当在IntentService和AsyncTask之间共享代码时,使用Realm会出现“从错误线程访问”的错误(Android)。

18

我有一些代码可以下载“当前”对象的JSON。但是这个代码需要被一个IntentService调用,每当闹钟响起时(当应用程序没有运行任何UI时),也需要被AsyncTask调用,而当应用程序正在运行时。

然而,我遇到了一个错误,Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.。然而,我不明白为什么这个堆栈跟踪出现在不同的线程上。

我通过将所有共享代码复制并直接粘贴到DownloadDealService的onHandleIntent方法中来消除了错误,但这非常混乱,而且我正在寻找一种更好的解决方案,不需要复制代码。

有什么方法可以消除这个错误,而不需要复制代码吗?谢谢。

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方法:

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);
    }
}
3个回答

21

[已更新] 基于附加信息

RealmDatabase.getInstance()正在返回在主线程上创建的Realm实例。而在IntentService的线程上使用了该实例,导致崩溃。

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


您无法在线程之间传递Realm对象。您可以做的是通过对象的唯一标识符(即@PrimaryKey)来传递它,并在另一个线程上通过其ID获取该对象。就像这样:realm.where(YourRealmModel.class).equalTo("primaryKeyVariable", id).findFirst()。

有关更多详细信息,请查看Realm的官方文档和示例:


你能看一下我的代码告诉我在哪里我在线程之间传递了任何东西吗?我不明白。我真的以为我在同一个线程上。据我所知,我只在线程之间共享方法,没有在线程之间传递任何领域对象。 - Rock Lee
过去,我的AsyncMethod实际上一直工作正常,即使通过onPostExecute()回调将Realm对象传递到UI线程,然后从那里调用Util.java类的静态方法也是如此。 - Rock Lee
2
我怀疑你在 RealmDatabase 类中进行了一些自定义的 Realm 实例缓存,导致你从不同的线程调用它而非最初获取它的那个线程。只需使用 Realm.getDefaultInstance() 即可。 - Roberto Artiles Astelarra
1
请看这里。Realm实例只能在创建它的线程上使用。在您的情况下,它是在主线程上创建的,但在IntentService线程中使用。 - Roberto Artiles Astelarra
2
我通过在我的IntentService中使用Realm.getInstance(this)并将其传递给Util方法来解决了这个问题(因为这些方法无法访问线程上下文)。现在,Util方法可以被多个线程使用,并且每个线程都有一个正确的Realm实例。 - Rock Lee
显示剩余4条评论

1
如果您在IntentService中使用Realm,您将使用新的Realm。例如:
Realm.getInstance(RealmConfiguration config) 

我在IntentService中使用了Realm。

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

0
问题在于您正在从不同的线程调用方法,您必须使用相同的线程,创建自己的 HandlerThread。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接