2015-12-26 3 views
4

Я пытаюсь использовать Realm.io в Android-приложении, но, чтобы оставаться в безопасности, я хотел бы абстрагировать уровень БД, чтобы в случае необходимости я может вернуться к стандартной базе данных на базе SQLite без перезаписи большинства приложений.Правильный способ абстрактного царства в Android-приложениях

Я однако найти его трудно правильно абстрактную Сферу из-за его особый характер:

  • Когда привязаны к области, RealmObjects являются прокси, так что я не могу передать их вокруг, как они были POJOs.
  • Всех экземпляры Realm должны быть надлежащим образом открываются и закрываются для каждого потока они используются.

Я прибег к использованию в последнее время Realm.copyFromRealm() API вместо огибает RealmObjects привязанного к Realm чтобы обойти эти ограничения, но таким образом я думаю, что я теряю все преимущества использования сферы (я?).

Любые предложения?

ответ

1

Я постараюсь ответить на ваше первое замешательство. Там действительно нет необходимости проходить вокруг RealmObject сек через фактически, или в других условиях, не нуждается в них не будучи parcelable или serializable

Что вы должны сделать, это пройти вокруг конкретного primary id или другого параметра этого конкретного RealmObject с помощью намерения, так что вы можете снова запросить этот RealmObject в следующем действии.

Например предположим, вы передаете primaryId при запуске активности,

Intent intent = new Intent(this, NextActivity.class); 
intent.putExtra(Constants.INTENT_EXTRA_PRIMARY_ID, id); 

Теперь внутри NextActivity получить идентификатор от намерения и просто запрос RealmObject, т.е.

int primaryId = getIntent().getIntExtra(Constants.INTENT_EXTRA_PRIMARY_ID, -1); 
YourRealmObject mObject = realm.where(YourRealmObject.class).equalTo("id", primaryId).findFirst(); 

See, вы получили ваш объект в новой активности, а не беспокоиться о прохождении объекта вокруг.

Я не знаю, с какими конкретными проблемами вы сталкиваетесь с открытием и закрытием объектов сферы. Вы попробовали Realm's Transaction blocks? Если вы используете это, вам не нужно полагаться на открытие и закрытие.

Надеюсь, это поможет.

Update

Поскольку вы ищете абстракции,

Просто создать класс как DbUtils и есть метод, там как getYourObjectById, а затем передать выше идентификатор и получить свой объект в ответ. Это делает его абстрактным в некотором роде. Затем вы можете сохранить этот класс и метод и просто изменить содержимое метода, если вы когда-либо переключитесь на другое решение для базы данных.

+0

Это шаблон, который я пытался избежать: вызов методов Realm внутри логики, не связанной с БД. Основная идея заключалась в том, чтобы абстрагировать это так, что если завтра я переключусь на другую базу данных, мне не придется переписывать такой код. –

+0

@MarcoRomano. Тогда просто создайте класс, подобный 'DbUtils', и у вас есть метод, подобный' getYourObjectById', а затем перейдите над 'id' и получите свой объект в' return'. Разве это не сделает его абстрактным? Затем вы можете сохранить этот класс и метод и просто изменить содержание метода, если вы когда-либо переключитесь на другой db –

+0

Вот что я в итоге сделал. Благодаря! –

1

Предоставление другого мнения, так как я проголосовал за другой ответ.Я хотел бы сначала обсудить вашей цель

в случае необходимости, я могу вернуться к стандартной БД на основе SQLite без переписывания большинства из приложения

Realm не соответствует стандарту SQLite- основанная БД. То, что вы ищете в этом случае, это что-то вроде StorIO. Realm разработан таким образом, что вы должны использовать RealmObject в качестве базы для своих моделей. Если вы просто хотите получить данные из таблицы, Realm не для вас.

Теперь, после фиаско Parse, появилась необходимость в абстрагировании сторонней библиотеки. Итак, если это ваша мотивация, вам действительно нужно обернуть каждую вещь, которую вы используете из Королевства. Что-то вроде этого:

public class Db { 

    public class Query<M extends RealmObject> { 

    private final RealmQuery<M> realmQuery; 

    Query(Db db, Class<M> clazz) { 
     this.realmQuery = RealmQuery.createQuery(db.realmInstance, clazz); 
    } 

    public Query<M> equalTo(String field, Integer value) { 
     realmQuery.equalTo(field, value); 
     return this; 
    } 

    public Results<M> findAll() { 
     return new Results<>(realmQuery.findAll()); 
    } 

    public M findFirst() { 
     return realmQuery.findFirst(); 
    } 
    } 

    public class Results<M extends RealmObject> extends AbstractList<M> { 

    private final RealmResults<M> results; 

    Results(RealmResults<M> results) { 
     this.results = results; 
    } 

    public void removeLast() { 
     results.removeLast(); 
    } 

    @Override 
    public M get(int i) { 
     return results.get(i); 
    } 

    @Override 
    public int size() { 
     return results.size(); 
    } 
    } 

    public interface Transaction { 
    void execute(); 

    abstract class Callback { 
     public abstract void onSuccess(); 

     public void onError(Throwable error) { 
     } 
    } 
    } 

    private Realm realmInstance; 

    Db() { 
    realmInstance = Realm.getDefaultInstance(); 
    } 

    public void executeTransaction(@NonNull Db.Transaction dbTransaction) { 
    realmInstance.executeTransaction(realm -> dbTransaction.execute()); 
    } 

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

На данный момент, вы просто определить какие операции, которые вы используете в своем приложении на Db класса. И, если вы похожи на меня, вы, вероятно, не используете их всех, так что с вами все будет в порядке.

Это довольно? Нет. Это необходимо? Зависит от того, чего вы хотите достичь.

Редактировать: для бонусной точки, которые Db реализовать AutoCloseable:

try(Db db = new Db()) { 
    db.executeTransaction(() -> /*...*/); 
} 
+0

'Realm не эквивалентен стандартной базе данных на базе SQLite'. Это больше, чем база данных SQLite, и думать о ней как о «просто SQLite db» означает, что вы сделаете ошибочную абстракцию для нее. – EpicPandaForce

+0

@EpicPandaForce вы бы считали абстракцию такой приемлемой? Я сам что-то сделал. –

+0

На самом деле это довольно разумная абстракция (обертывание всех интерфейсов Realm по интерфейсу), мне было лениво делать это самостоятельно, но это допустимый вариант. Вам просто нужно убедиться, что вы выполняете 'try (Db db = new Db())' вещь в своих соответствующих потоках, поэтому управление жизненным циклом Realm невозможно отбросить. Благодаря новейшим компонентам архитектуры Google I/O 2017 * Architecture другая альтернатива заключается в том, что вы можете скрыть Realm в собственных реактивных абстракциях Android. – EpicPandaForce

2

С последними Google I/O 2017 объявления для Android Architectural Components, правильный путь к абстрактному Realm в Android приложений является:

1.) Жизненный цикл экземпляра Realm управляется классом ViewModel, и он закрыт в onCleared() метод

2.) RealmResults - это MutableLiveData<List<T>>, поэтому вы можете создать класс RealmLiveData<T>, который обертывает RealmResults<T>.

Таким образом, вы можете создать модель представления, как это:

// 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; 
    } 
} 

или вы можете разделить их в сферу ViewModel и царств livedata на основе this article:

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(); 
    } 
} 

В любом случае, вы 've завернутое автоматическое обновление Realm и ленивый результат, полученный в LiveData и ViewModel, отдельно от фрагментов/адаптеров:

// 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 является список прокси-объектов, мутирует на месте, и это имеет слушателей изменений.

Так что вам нужно либо обернув его как текучие с последним противодавлением, сродни

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); 
} 
+0

Хорошее объяснение, но у вас есть TL; DR между вышеуказанной статьей, которую вы связали, и этот: https://hackernoon.com/musing-on-architectural-components-and-realm-and-room-and-a -44-ая-чистая-архитектура-880c8df55abf – Phileo99

+0

@ Phileo99 это tldr – EpicPandaForce

+0

@EpicPandaForce Эй, мы поговорили на прошлой неделе об абстракциях в области. Я закончил свою работу и подключил ее к модели DAO/view. Не могли бы вы дать это взглянуть и сообщить мне свои мысли? Я хочу, чтобы я не делал критических ошибок. Благодаря! https://gist.github.com/danielchristopher1/0fa253dcec60ecd77b316543efe6dc73 –

Смежные вопросы