android - 在 Android 应用程序中抽象 Realm 的正确方法

标签 android realm abstraction

我正在 Android 应用程序中试用 Realm.io,不过,为了安全起见,我想抽象 DB 层,以便在需要时可以切换回基于 SQLite 的标准无需重写大部分应用程序的数据库。

然而,由于 Realm 的特殊性质,我发现很难正确抽象它:

  • 当绑定(bind)到一个 Realm 时,RealmObjects 是代理,所以我不能像 POJO 一样传递它们。
  • 所有 Realm 实例都需要在使用它们的每个线程中正确打开和关闭。

我已经使用最近的 Realm.copyFromRealm() API 而不是传递与 Realm 绑定(bind)的 RealmObjects 来绕过这些限制,但我认为这样我就失去了使用 realm 的所有好处(是吗? ).

有什么建议吗?

最佳答案

随着最新的 Google I/O 2017 公告 Android Architectural Components ,在 Android 应用程序中抽象 Realm 的正确方法是:

1.) Realm 实例生命周期由 ViewModel 管理类,并在 onCleared() 中关闭方法

2.) RealmResults 是一个 MutableLiveData<List<T>> , 所以你可以创建一个 RealmLiveData<T>包装 RealmResults<T> 的类.

因此,您可以像这样创建一个 View 模型:

// based on  https://github.com/googlesamples/android-architecture-components/blob/178fe541643adb122d2a8925cf61a21950a4611c/BasicSample/app/src/main/java/com/example/android/persistence/viewmodel/ProductListViewModel.java
public class ProductListViewModel {
    private final MutableLiveData<List<ProductEntity>> observableProducts = new MutableLiveData<>();

    Realm realm;
    RealmResults<ProductEntity> results;
    RealmChangeListener<RealmResults<ProductEntity>> realmChangeListener = (results) -> {
        if(results.isLoaded() && results.isValid()) { // you probably don't need this, just making sure.
            observableProducts.setValue(results);
        }
    };

    public ProductListViewModel() {
        realm = Realm.getDefaultInstance();             
        results = realm.where(ProductEntity.class).findAllSortedAsync("id"); 
          // could use a Realm DAO class here
        results.addChangeListener(realmChangeListener);

        observableProducts.setValue(null); // if using async query API, the change listener will set the loaded results.
    }

    public LiveData<List<ProductEntity>> getProducts() {
        return observableProducts;
    }

    @Override
    protected void onCleared() {
        results.removeChangeListener(realmChangeListener);
        realm.close();
        realm = null;
    }
}

或者您可以根据this article将它们分成 Realm View 模型和 Realm 实时数据。 :

public class LiveRealmData<T extends RealmModel> extends LiveData<RealmResults<T>> {

    private RealmResults<T> results;
    private final RealmChangeListener<RealmResults<T>> listener = 
        new RealmChangeListener<RealmResults<T>>() {
            @Override
            public void onChange(RealmResults<T> results) { setValue(results);}
    };

    public LiveRealmData(RealmResults<T> realmResults) {
        results = realmResults;
    }

    @Override
    protected void onActive() {
        results.addChangeListener(listener);
    }

    @Override
    protected void onInactive() {
        results.removeChangeListener(listener);
    }
}

public class CustomResultViewModel extends ViewModel {

    private Realm mDb;
    private LiveData<String> mLoansResult;

    public CustomResultViewModel() {
        mDb = Realm.getDefaultInstance();
        mLoansResult = RealmUtils.loanDao(mDb).getAll();
    }

    public LiveData<String> getLoansResult() {
        return mLoansResult;
    }

    @Override
    protected void onCleared() {
        mDb.close();
        super.onCleared();
    }
}

无论哪种方式,您都将 Realm 的自动更新和延迟加载结果集包装到 LiveData 和 ViewModel 中,与 fragment/适配器分开:

// based on https://github.com/googlesamples/android-architecture-components/blob/178fe541643adb122d2a8925cf61a21950a4611c/BasicSample/app/src/main/java/com/example/android/persistence/ProductListFragment.java
public class ProductListFragment extends LifecycleFragment {
    private ProductAdapter productAdapter;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        //...
        productAdapter = new ProductAdapter(mProductClickCallback);
        //...
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final ProductListViewModel viewModel =
                ViewModelProviders.of(this).get(ProductListViewModel.class); // <-- !

        subscribeUi(viewModel);
    }

    private void subscribeUi(ProductListViewModel viewModel) {
        // Update the list when the data changes
        viewModel.getProducts().observe(this, (myProducts) -> {
            if (myProducts == null) {
                // ...
            } else {
                productAdapter.setProductList(myProducts);
                //...
            }
        });
    }
}

但是,如果您使用 Android 架构组件,即便如此,您也需要牢记:

RealmResults is a list of proxy objects that mutates in place, and it has change listeners.

所以你需要的是将它包装为具有最新背压的 Flowable,类似于

private io.reactivex.Flowable<RealmResults<T>> realmResults() {
    return io.reactivex.Flowable.create(new FlowableOnSubscribe<RealmResults<T>>() {
        @Override
        public void subscribe(FlowableEmitter<RealmResults<T>> emitter)
                throws Exception {
            Realm observableRealm = Realm.getDefaultInstance();
            RealmResults<T> results = realm.where(clazz)./*...*/.findAllSortedAsync("field", Sort.ASCENDING);
            final RealmChangeListener<RealmResults<T>> listener = _results -> {
                if(!emitter.isDisposed()) {
                    emitter.onNext(_results);
                }
            };
            emitter.setDisposable(Disposables.fromRunnable(() -> {
                observableRealm.removeChangeListener(listener);
                observableRealm.close();
            }));
            observableRealm.addChangeListener(listener);
            emitter.onNext(observableRealm);
        }
    }, BackpressureStrategy.LATEST).subscribeOn(scheduler).unsubscribeOn(scheduler);

或创建您自己的 MutableLiveList界面。

public interface MutableLiveList<T> extends List<T> { 
     public interface ChangeListener {
         void onChange(MutableLiveList<T> list);
     }

     void addChangeListener(ChangeListener listener);
     void removeChangeListener(ChangeListener listener);
}

关于android - 在 Android 应用程序中抽象 Realm 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34473611/

相关文章:

java - 将反射与 Realm 结合使用 (Android)

oop - 抽象=封装+数据隐藏?

Java反射: Initialize object using interface but using String value for class name

java - Android 在某些部分显示文本语言错误

java.lang.IllegalStateException : Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2

ios - 在后台线程中将数据保存到 Realm 时发生崩溃。 | iOS | swift 4.2

c# - 我应该如何将我的商业模式与我的观点联系起来?

java - 我如何在 Android Studio 中获得行号

android - 在应用程序内使用外部应用程序 fragment/Activity

swift - Realm 不保存数据