在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从不正确的线程访问”错误
Posted
技术标签:
【中文标题】在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从不正确的线程访问”错误【英文标题】:Realm `access from incorrect thread` error when using shared code between IntentService and AsyncTask (Android) 【发布时间】:2016-02-26 07:49:57 【问题描述】:我有一些代码可以下载“当前”对象的 JSON。但是,每当警报响起时(当应用程序未运行任何 UI 时),IntentService 都需要调用相同的代码,并且在应用程序运行时也需要由 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);
【问题讨论】:
【参考方案1】:[更新]基于附加信息
RealmDatabase.getInstance() 正在返回在主线程上创建的 Realm 实例。这个实例被用在了 IntentService 的线程上。这导致了崩溃。
Realm 实例不能在除创建它们的线程之外的任何其他线程上使用。
你不能在线程之间传递 Realm 对象。您可以做的是传递对象的唯一标识符(即@PrimaryKey),然后在另一个线程上通过其 id 获取对象。像这样:realm.where(YourRealmModel.class).equalTo("primaryKeyVariable", id).findFirst()。
欲了解更多详情,请查看 Realm 的官方文档和示例:
跨线程使用领域https://realm.io/docs/java/latest/#threading 线程示例https://github.com/realm/realm-java/tree/master/examples/threadExample【讨论】:
你能看看我的代码并告诉我我在线程之间传递了什么吗?我不明白。老实说,我以为我在同一个线程上。据我所知,我只在线程之间共享方法,没有在线程之间传递任何领域对象。 很难理解您的代码示例中的内容。但我假设您在 AsyncTask 的 doInBackround 中获取结果对象(在工作线程上执行),然后将其作为结果传递给 onPostExecute() (在主线程上执行)。然后尝试通过剩余()方法从那里访问它,这会炸毁你的应用程序。 我怀疑您正在对 RealmDatabase 类中的 Realm 实例进行一些自定义缓存。这导致您从与您获得它的原始线程不同的线程调用它。只需使用 Realm.getDefaultInstance()。 给你。 Realm 实例只能在它创建的线程上使用。在您的情况下,它是在主线程上创建的,但在 IntentService 线程中使用。 我能够通过在我的IntentService
中使用Realm.getInstance(this)
来解决该问题,并将其传递给Util 方法使用(因为这些方法无法访问线程上下文)。所以现在,Util 方法可以被多个线程使用,每个线程都有一个正确的 Realm 实例。【参考方案2】:
如果你在 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
【讨论】:
【参考方案3】:问题是你从不同的线程调用方法,你必须使用同一个线程,创建你自己的HandlerThread。
【讨论】:
以上是关于在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从不正确的线程访问”错误的主要内容,如果未能解决你的问题,请参考以下文章