2016-02-26 20 views
12

Trong các dự án Android của tôi, tôi sử dụng realm làm công cụ lưu trữ dữ liệu của mình. Tôi thích nó!
Tôi cũng sử dụng RxJava bởi vì nó làm cho "luồng" dễ dàng hơn nhiều, và tôi thực sự thích toàn bộ "suy nghĩ phản ứng". Tôi thích nó!
Lĩnh vực, RxJava, asObservable() và doOnUnsubscribe()

Tôi sử dụng mẫu MVP + một số ý tưởng "Kiến trúc sạch" để xây dựng ứng dụng của mình.

My Interactors là những người duy nhất biết về Realm. Tôi phơi bày dữ liệu với sự giúp đỡ của quan sát được, như thế này:

@Override 
public Observable<City> getHomeTown() { 
    final Realm realm = Realm.getDefaultInstance(); 
    return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable() 
      .doOnUnsubscribe(new Action0() { 
       @Override 
       public void call() { 
        realm.close(); 
       } 
      }) 
      .compose(new NullIfNoRealmObject<City>()); 
} 

Vấn đề là doOnUnsubscribe tác dụng phụ của tôi được gọi trước khi Realm có thể làm điều này, xử lý tiếp xúc quan sát:

Caused by: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable. 
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:344) 
at io.realm.RealmResults.removeChangeListener(RealmResults.java:818) 
at io.realm.rx.RealmObservableFactory$3$2.call(RealmObservableFactory.java:137) 
at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71) 
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124) 
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113) 
at rx.Subscriber.unsubscribe(Subscriber.java:98) 
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124) 
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113) 
at rx.Subscriber.unsubscribe(Subscriber.java:98) 
at rx.subscriptions.CompositeSubscription.unsubscribeFromAll(CompositeSubscription.java:150) 
at rx.subscriptions.CompositeSubscription.unsubscribe(CompositeSubscription.java:139) 
at ro.tudorluca.realm.sandbox.city.CityPresenter.onDestroy(CityPresenter.java:62) 
at ro.tudorluca.realm.sandbox.city.CityActivity.onDestroy(CityActivity.java:35) 

tôi đã tạo một dự án sandbox cho trường hợp sử dụng này.

Tôi thực sự thích sử dụng Realm + RxJava, nhưng tôi dường như không thể tìm thấy một giải pháp sạch để close sơ thẩm Realm khi tôi unsubscribe (Tôi thường bỏ đăng ký khi hoạt động bị phá hủy). Bất kỳ ý tưởng?

Sửa 1: https://github.com/realm/realm-java/issues/2357
Chỉnh sửa 2: nhờ vào đội ngũ lãnh vực rất tích cực, đã có một pull request để khắc phục vấn đề này.

Trả lời

1

21 giờ sau đó và đây là những gì tôi đã đưa ra:

@Override 
public Observable<City> getHomeTown() { 
    return getManagedRealm() 
      .concatMap(new Func1<Realm, Observable<City>>() { 
       @Override 
       public Observable<City> call(Realm realm) { 
        return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable() 
          .compose(new NullIfNoRealmObject<City>()); 
       } 
      }); 
} 

private static Observable<Realm> getManagedRealm() { 
    return Observable.create(new Observable.OnSubscribe<Realm>() { 
     @Override 
     public void call(final Subscriber<? super Realm> subscriber) { 
      final Realm realm = Realm.getDefaultInstance(); 
      subscriber.add(Subscriptions.create(new Action0() { 
       @Override 
       public void call() { 
        realm.close(); 
       } 
      })); 
      subscriber.onNext(realm); 
     } 
    }); 
} 

tôi đã cố gắng một cái gì đó như thế này trước khi đăng câu hỏi về stackoverflow, nhưng sai lầm của tôi đã sử dụng flatMap(), thay vì concatMap().

Không giống như flatMap(), concatMap() sẽ giữ thứ tự của các khí thải đó, trong trường hợp của tôi, có nghĩa là Action0 -> realm.close() của tôi sẽ là hành động cuối cùng được gọi sau khi hủy đăng ký từ suối, sau Realm của Action0 -> results.removeChangeListener(listener) được gây ra vấn đề.

Ví dụ đầy đủ có thể được tìm thấy trên github.

Chỉnh sửa: nhờ nhóm lĩnh vực hoạt động rất tích cực, đã có pull request để khắc phục sự cố này.

+0

Hãy xem xét gói Realm bằng cách sử dụng Observable # bằng cách sử dụng, điều này sẽ đơn giản hóa phương thức getManagedRealm() của bạn: http://pastebin.com/CrzryvCq –

+0

Tôi thử giải pháp của bạn và tôi gặp sự cố: quá nhiều tệp mở trong lĩnh vực –

0

Vì bạn nói rằng chỉ có INteractor "biết" về khuôn khổ Realm Tôi có thể nói không còn trả về một đối tượng Realm quản lý, thay vì trả về một bản sao không được quản lý của các kết quả sử dụng copyFromRealm. Bằng cách đó bạn không phải quan tâm đến trường hợp của Realm đang mở hoặc đóng trong Presenter.

Đồng thời tôi sẽ để cho các Presenter chọn thời tiết các cuộc gọi nên được thực hiện không đồng bộ hay không kể từ RxJava nào đó khá mát mẻ và dễ dàng và bạn sẽ không có vấn đề gọi phương thức tải INteractor trong thread khác (có thể tránh được bằng cách sử dụng Loopers nhưng tại sao quá phức tạp tình hình nếu bạn có thể làm cho nó đơn giản hơn: P).

Vì vậy, tôi sẽ đi cho:

Override 
public Observable<City> getHomeTown() { 
    final Realm realm = Realm.getDefaultInstance(); 
    City city = realm.where(City.class).equalTo("name", "Cluj-Napoca").findFirst(); 

    // make sure we don't send back Realm stuff, this is a deep copy that will copy all referenced objects (as the method doc says) 
    City cityUnmanaged = realm.copyFromRealm(city); 

    // safe to close the realm instance now 
    realm.close(); 

    return Observable.just(cityUnmanaged); 
} 

Tôi tò mò muốn xem các tùy chọn hơn :).

+1

Trả lại một quan sát , Interactor của tôi hoàn toàn bao hàm "suy nghĩ phản ứng". Khi bạn thực hiện truy vấn, Interactor sẽ trả về luồng sẽ đẩy dữ liệu khi nó sẵn sàng và sẵn sàng. Khi có dữ liệu mới, luồng trả về sẵn sàng đẩy bạn dữ liệu đó. Trong trường hợp của tôi, tôi sẽ nhận được một thành phố được cập nhật mỗi lần một người nào khác đẩy các thay đổi vào vương quốc. "Tự động làm mới" là một tính năng cốt lõi của Realm và một tính năng rất hay. –

+0

Ngoài ra, copyFromRealm() là ok cho bản demo của tôi, bởi vì các đối tượng của tôi là nhỏ không có mối quan hệ. Nhưng trong cuộc sống thực, tôi có một biểu đồ phức tạp của các đối tượng và tôi thực sự không muốn tải tất cả chúng vào bộ nhớ. "Lazy + no-copy data" là một tính năng cốt lõi khác của Realm mà bạn nên tận dụng tối đa bất cứ khi nào bạn có thể. –

+0

Lĩnh vực có giới hạn "luồng" và bạn không thể sử dụng RxJava để xử lý các truy vấn đa luồng. Có một dự án ví dụ tốt mà bạn có thể khám phá để biết thêm về gotchas: https://github.com/realm/realm-java/blob/master/examples/rxJavaExample/src/main/java/io/realm/examples/rxjava/gotchas /GotchasActivity.java –

0

Theo tôi, một trong những điều quan trọng để chăm sóc trong một kiến ​​trúc tốt là mô đun. Tất cả các mô-đun chính (hoặc thư viện) nên được phân lập từ phần còn lại của mã. Vì Realm, RealmObject hoặc RealmResult không thể được truyền qua các chủ đề, nó thậm chí còn quan trọng hơn để làm cho Realm & Các hoạt động liên quan đến lĩnh vực được phân lập từ phần còn lại của mã.

Lưu ý đến triết lý này, tôi đã đưa ra cách tiếp cận sau.

Đối với mỗi lớp jsonModel, chúng ta tạo một lớp realmModel và một lớp DAO (Data Access Object). Ý tưởng ở đây là khác hơn là lớp DAO không có lớp nào phải biết hoặc truy cập các thực thể realmModel hoặc Realm. DAO lớp mất jsonModel, chuyển đổi nó thành realmModel, thực hiện các hoạt động đọc/ghi/chỉnh sửa/xóa & cho các hoạt động đọc DAO chuyển đổi kết quả realmModel thành jsonModel và trả về chúng.

Bằng cách này, bạn dễ dàng duy trì lĩnh vực, tránh tất cả các vấn đề liên quan đến chuỗi, dễ dàng kiểm tra và gỡ lỗi.

Đây là một bài viết về thực hành tốt nhất Realm với một kiến ​​trúc tốt https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

Cũng là một dự án mẫu chứng minh Lồng ghép Realm trên Android với MVP (Model View Presenter), RxJava, Retrofit, Dagger, Chú thích & Testing. https://github.com/viraj49/Realm_android-injection-rx-test

Các vấn đề liên quan