从不正确的线程Android Retrofit2 RxJava访问领域

时间:2016-11-08 21:21:14

标签: android multithreading realm retrofit rx-java

我正在尝试将我的对象从Retrofit直接保存到Realm,但总是得到错误:“Realm访问来自错误的线程”。

这是我的代码:

public class RestaurantRepositoryRetrofit implements IRestaurantRepository {

private RestaurantApi mApi;
private Realm realm;
private IMapper<RestaurantJson,Restaurant> mRestaurantMapper;

public RestaurantRepositoryRetrofit(IMapper<RestaurantJson, Restaurant> restaurantMapper) {
    mApi = ApiProvider.getApi().create(RestaurantApi.class);
    mRestaurantMapper = restaurantMapper;
    // Get a Realm instance for this thread
    realm = Realm.getDefaultInstance();
**}
@Override
public Observable<Restaurant> getRestaurantById(String restaurantId) {**

    return mApi.getRestaurantById(restaurantId)
            .map(new Func1<RestaurantJson, Restaurant>() {
                @Override
                public Restaurant call(RestaurantJson restaurantJson) {
                    realm.executeTransaction(new Realm.Transaction() {
                        @Override
                        public void execute(Realm realm) {

                            realm.copyToRealm(restaurantJson);
                        }
                    });
                    return mRestaurantMapper.transform(restaurantJson);
                }
            });
    }
}

3 个答案:

答案 0 :(得分:1)

realm = Realm.getDefaultInstance();将返回创建对象的线程的实例。但是Observable.map()在observable发送消息的线程上被调用。

由于可观察性来自Retrofit,因此可以有两个选项:

  1. 如果在未指定调度程序的情况下创建了CallAdapter,则它将在调用的线程Observable.subscribe上执行。
  2. 如果使用特定的调度程序创建CallAdapter,它将在该线程上执行。
  3. 如果应用的选项与创建对象的选项不同,则将抛出“来自错误线程的域访问”错误。

答案 1 :(得分:1)

您应该在接收API结果的后台线程上打开Realm实例。

return mApi.getRestaurantById(restaurantId)
        .map(new Func1<RestaurantJson, Restaurant>() {
            @Override
            public Restaurant call(RestaurantJson restaurantJson) {
                try(Realm realm = Realm.getDefaultInstance()) {
                     realm.executeTransaction(new Realm.Transaction() {
                         @Override
                         public void execute(Realm realm) {
                             realm.copyToRealm(restaurantJson);
                         }
                     });
                     return mRestaurantMapper.transform(restaurantJson);
                } 
            }
        });

虽然如果您打算返回托管的RealmObject,您应该从保存的代理中映射出ID,然后在主线程上观察并使用ID查询Realm的UI线程实例。

答案 2 :(得分:0)

领域is thread confined,这意味着您需要确保在您获得引用的同一个线程上对Realm对象和realm实例进行调用。您可能希望使用RxAndroid中的调度方法observeOn()来确保在与realm::executeTransaction()实例相同的线程上调用realm