【问题标题】:Realm accessed from incorrect thread, with Dagger使用 Dagger 从不正确的线程访问领域
【发布时间】:2017-09-23 07:35:53
【问题描述】:

我目前正在一个简单的项目中学习 Dagger、RxJava、Realm 和 MVP。

这个应用程序可以做的基本上是它可以查看、添加、删除和更新数据库中的数据,我正在使用 Realm。

我决定遵循 MVP 架构和应用存储库模式以及后端层的数据操作。

为了进一步学习,我为架构中的依赖注入添加了 Dagger。

在此之前,我开发的应用程序没有应用 MVP 或存储库模式,甚至没有考虑 Dagger 和 RxJava。一切似乎都运行良好,没有来自 Realm 线程系统的任何错误。也许是因为我把所有东西都放在一个班级里。

所以,既然我正在放弃这种方法,我现在在新方法中实施它时遇到了麻烦,我认为这种方法更松散耦合,如果实施正确应该会更好。


介绍完毕,让我们回到正题。

我现在面临的问题是 Realm 总是给我这个错误:

抛出异常:从错误的线程访问领域。

我怀疑我的 Dagger 图表没有得到妥善管理(尤其是在提供 Realm 实例时),因此每当我查询数据时,都会出现错误。

所以,我的 Dagger 组件如下所示:

@Singleton
@Component(modules = {ContextModule.class, RepositoryModule.class, PresenterModule.class})
public interface AppComponent {

    /* Inejct application */
    void inject(FourdoApp fourdoApp);

    /* Realm Helper */
    void inject(DatabaseRealm databaseRealm);

    /* Activity */
    void inject(MainActivity mainActivity);
    void inject(TaskDetailActivity taskDetailActivity);

    /* Presenter*/
    void inject(MainPresenter mainPresenter);
    void inject(TaskDetailPresenter taskDetailPresenter);

    /* Model repository*/
    void inject(TaskRepositoryImpl taskRepository);

}

里面RepositoryModule.class;

@Module
public class RepositoryModule {

    @Provides
    @Singleton
    Repository<Task> provideTaskRepository() {
        return new TaskRepositoryImpl();
    }

    // Here I provide DatabaseRealm class instance

    @Provides
    @Singleton
    public DatabaseRealm provideDatabaseRealm() {
        return new DatabaseRealm();
    }

}

不确定我这样做是否正确。您可以查看 DI here 的来源。

为了发生数据请求,在MainActivity 内部,我注入了MainPresenter 并调用onRequestData 接口向Presenter 请求它。从那里,Presenter 将为所述数据调用 Repository。

public class MainActivity extends BaseActivity implements MainContract.View {

    @Inject
    MainPresenter mainPresenter;

    // ...

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        // Injecting MainActivity class
        Injector.getAppComponent().inject(this);


        mainPresenter.attachView(this);


        // Requesting for data from Presenter
        mainPresenter.onRequestData();

    }

    // ...

    @Override
    public void onRequestDataSuccess(List<String> taskList) {
        doAdapter.addAll(taskList);
        doAdapter.notifyDataSetChanged();
    }
}

在 MainPresenter 中,我注入了 Repository 接口以从TaskRepositoryImpl 请求数据库中的真实数据。

public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {

    @Inject
    Repository<Task> taskRepository;

    public MainPresenter() {
        Injector.getAppComponent().inject(this);
    }

    @Override
    public void onRequestData() {
        requestData();
    }

    private void requestData() {

        taskRepository.findAll()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .map(this::mapToStringList)
                .subscribe(new Observer<List<String>>() {

                    @Override
                    public void onNext(List<String> strings) { // Error in this line
                        if (strings.size() > 0) {
                            mView.onRequestDataSuccess(strings);
                        } else {
                            mView.showEmpty();
                        }
                    }
                });
    }
}

TaskRepositoryImpl 内部,这是我执行findAll 的方法,它应该从DatabaseRealm 返回数据:

@Override
public Observable<List<Task>> findAll() {
    return Observable.create(subscriber -> {
        try {
            List<Task> models = databaseRealm.findAll(Task.class);

            subscriber.onNext(models);
            subscriber.onComplete();
        } catch (Exception e) {
            subscriber.onError(e);
        }
    });
}

DatabaseRealm的代码如下:

public class DatabaseRealm {
    @Inject
    Context context;

    RealmConfiguration realmConfiguration;

    public DatabaseRealm() {
        Injector.getAppComponent().inject(this);
    }

    public void setup() {
        if (realmConfiguration == null) {
            Realm.init(context);
            realmConfiguration = new RealmConfiguration.Builder()
                    .deleteRealmIfMigrationNeeded()
                    .build();
            Realm.setDefaultConfiguration(realmConfiguration);
        } else {
            throw new IllegalStateException("Realm already configured");
        }
    }

    public Realm getRealmInstance() {
        return Realm.getDefaultInstance();
    }

    public <T extends RealmObject> T add(T model) {
        Realm realm = getRealmInstance();
        realm.beginTransaction();
        realm.copyToRealm(model);
        realm.commitTransaction();
        return model;
    }

    public <T extends RealmObject> T update(T model) {
        Realm realm = getRealmInstance();
        realm.beginTransaction();
        realm.copyToRealmOrUpdate(model);
        realm.commitTransaction();
        return model;
    }

    public <T extends RealmObject> T remove(T model) {
        Realm realm = getRealmInstance();
        realm.beginTransaction();
        realm.copyToRealm(model);
        realm.deleteAll();
        realm.commitTransaction();
        return model;
    }

    public <T extends RealmObject> List<T> findAll(Class<T> clazz) {
        return getRealmInstance().where(clazz).findAll();
    }

    public void close() {
        getRealmInstance().close();
    }
}

这个有缺陷的代码的完整源代码是正确的here

我想澄清一下,我对 Dagger 中使用的 Realm 实例了解有限。

我遵循this tutorial 的 Repository Design Pattern with Realm,但它不包括 Dagger 的依赖注入。

有人可以指导我为什么它总是告诉我从错误的线程调用 Realm?

【问题讨论】:

  • 我没有使用过匕首。但我猜这个问题是因为“领域不能跨线程访问对象”。我认为这是 Realm 的一个缺点。请根据这一点来看看。希望对您有所帮助。

标签: android realm mvp dagger-2


【解决方案1】:

我认为您因此而收到此错误:

@Override
public Observable<List<Task>> findAll() {
    return Observable.create(subscriber -> {
        try {
            List<Task> models = databaseRealm.findAll(Task.class);

            subscriber.onNext(models);
            subscriber.onComplete();
        } catch (Exception e) {
            subscriber.onError(e);
        }
    });
}

您订阅了io Thread,但您将databaseRealm 注入Main Thread

如果您在 observable 的创建中获得实例,则不会出现此错误。

@Override
public Observable<List<Task>> findAll() {
    return Observable.create(subscriber -> {
        try {
            Realm realm = getRealmInstance();
            List<Task> models = realm.findAll(Task.class);

            subscriber.onNext(models);
            subscriber.onComplete();
        } catch (Exception e) {
            subscriber.onError(e);
        }
    });
}

【讨论】:

    【解决方案2】:

    您只需在Application 类中设置一次RealmConfigration 并使用Realm.getDeafultInstance() 方法访问Realm 数据库

    使用 Dagger,您只需在构造函数中传递领域实例

    你可以关注这个Example并fork它

    它与您在此处发布的代码不完全相同。但它可能有助于更好地理解 MVP、RxJava 和 Realm 的 Dagger

    【讨论】:

    • 是的。你说的对。我只需要在应用程序中实例化一次领域。然后使用 Dagger 提供默认实例。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 2017-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    相关资源
    最近更新 更多