我正在 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/