2017-10-05 5 views
2

これは、受信トランザクションをRx Observableトランザクション内でRealmコールを使用する際に、Realm Threadingの問題を防止するにはどうすればよいですか?

RealmHelperRepo呼ばれるビューモデルであり、これはレルム呼び出しが行われRealmRepoクラスである

@PerActivity 
public class RoleSelectionViewModel extends BaseViewModel<RoleSelectionMvvm.View> implements RoleSelectionMvvm.ViewModel { 

    private Disposable roleGroupSubscription; 

    @Inject 
     public RoleSelectionViewModel(@AppContext Context context, HelperRepo helperRepo, ApiOAuth2 ApiOAuth2) { 

     this.mContext = context; 
     this.mUserRepo = userRepo; 
     this.mHelperRepo = helperRepo; 
     ApiOAuth2.initialize(); 
     this.mApiOAuth2 = ApiOAuth2; 

     this.mCurrentUser = mUserRepo.getByField("isLoggedIn", true, true); 
     if (mCurrentUser != null) { 
      this.mCurrentUserId = mCurrentUser.getId(); 
      this.mHelper = mHelperRepo.getByField("user.id", mCurrentUserId, true); 
    } 

    Observable<Response<ResponseHelper>> postHelperObservable = mApiOAuth2.postHelperRX(new Helper()); 
    Observable<Response<ResponseHelper>> getHelperObservable = mApiOAuth2.getHelperRX(mCurrentUserId); 

roleGroupSubscription = postRoleGroupsObservable 
        .subscribeOn(Schedulers.io()) 
        .observeOn(AndroidSchedulers.mainThread()) 
        .flatMap((response) -> { 
         if (response.isSuccessful()) { 
          ResponseHelper responseHelper = response.body(); 
          mHelper = responseHelper.getHelper(); 
          return Observable.just(mHelper); 
         } else if (response.code() == 409) { 
         // handle POST conflict (i.e. helper already exists) 
          return getHelperObservable; 
         } 

        }) 
        .subscribe((data) -> { 
         if (data instanceof Response<?>) { 
          // data came from getHelperObservable 
          Response response = (Response) data; 
          if (!response.isSuccessful()) { 
           ResponseHelper responseHelper = (ResponseHelper) response.body(); 
          mHelper = responseHelper.getHelper(); 
         else { 

          // data came from Observable.just(helper) 
          mApiOAuth2.getHelperRX(mCurrentUserId).subscribe(
            responseHelperResponse -> { 

             if (responseHelperResponse.isSuccessful()) { 

              String helperID = responseHelperResponse.body().getHelper().getId(); 
              Log.d("RealmCount", "save: " + Realm.getLocalInstanceCount(realmProvider.get().getConfiguration())); 
              mHelper.setId(helperID); 
              mHelper.setUser(mCurrentUser); 
--------> // when mHelperRepo.save(mHelper) is called, it goes to RealmHelperRepo to save and 
--------> // thus triggering mRealm.executeTransaction causing Realm threading 
              mHelperRepo.save(mHelper); 
             } 
             saveAndBegin(); 
            }, 
            Throwable::printStackTrace); 
            }); 

HelperRepoインタフェースの実装です。

@PerApplication 
public class RealmHelperRepo implements HelperRepo { 

    private final Provider<Realm> mRealmProvider; 
    private Realm mRealm; 

    @Inject 
    public RealmHelperRepo(Provider<Realm> realmProvider) { 
     this.mRealmProvider = realmProvider; 
     this.mRealm = mRealmProvider.get(); 
} 


    @Override 
    public void save(Helper helper) { 
     if (mRealm != null) { 
---------> // code runs into threading issue here when a realmThread executeTransaction is called 
     mRealm.executeTransaction(r -> r.copyToRealmOrUpdate(helper)); 
     } 
    } 

ここには何かがありますか?フラットマップの代わりに使用しなければならない他のRx関数?スレッドの問題に遭遇せずに観測可能なデータを保存する他の方法はありますか?助けて!

答えて

2

ここには何かがありますか?

Realm

インスタンスは、参照カウントスレッドローカルインスタンスを表します。これはグローバルなものではなく、getInstance()によって開かれた後、close()によって閉じられる「ローカルインスタンス」です。

Realmインスタンスは、バックグラウンドスレッドからアクセスできないため、シングルトンとして初期化することはできません。


たとえば、スレッドローカルのRealmインスタンスを開くことができるシングルトンレルムマネージャクラスを提供できます。

/** 
* The RealmManager allows creating a singleton Realm manager which can open thread-local instances. 
* 
* It also allows obtaining the open thread-local instance without incrementing the reference count. 
*/ 
@PerApplication 
public class RealmManager { 
    private final ThreadLocal<Realm> localRealms = new ThreadLocal<>(); 

    @Inject 
    RealmManager() { 
    } 

    /** 
    * Opens a reference-counted local Realm instance. 
    * 
    * @return the open Realm instance 
    */ 
    public Realm openLocalInstance() { 
     checkDefaultConfiguration(); 
     Realm realm = Realm.getDefaultInstance(); // <-- maybe configuration should be constructor parameter 
     if(localRealms.get() == null) { 
      localRealms.set(realm); 
     } 
     return realm; 
    } 

    /** 
    * Returns the local Realm instance without adding to the reference count. 
    * 
    * @return the local Realm instance 
    * @throws IllegalStateException when no Realm is open 
    */ 
    public Realm getLocalInstance() { 
     Realm realm = localRealms.get(); 
     if(realm == null) { 
      throw new IllegalStateException(
        "No open Realms were found on this thread."); 
     } 
     return realm; 
    } 

    /** 
    * Closes local Realm instance, decrementing the reference count. 
    * 
    * @throws IllegalStateException if there is no open Realm. 
    */ 
    public void closeLocalInstance() { 
     checkDefaultConfiguration(); 
     Realm realm = localRealms.get(); 
     if(realm == null) { 
      throw new IllegalStateException(
        "Cannot close a Realm that is not open."); 
     } 
     realm.close(); 
     // noinspection ConstantConditions 
     if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) { 
      localRealms.set(null); 
     } 
    } 

    private void checkDefaultConfiguration() { 
     if(Realm.getDefaultConfiguration() == null) { 
      throw new IllegalStateException("No default configuration is set."); 
     } 
    } 
} 

あなたはそれだけでRealm.getDefaultInstance()電話を隠し、あなたも内部RealmCacheの参照カウントをインクリメントすることなく、あなたのスレッドローカルインスタンスを取得することができますので、実際のあまりないので

@PerApplication 
public class RealmHelperRepo implements HelperRepo { 
    private final RealmManager realmManager; 

    @Inject 
    public RealmHelperRepo(RealmManager realmManager) { 
     this.realmManager = realmManager; 
    } 


    @Override 
    public void save(Helper helper) { 
     try(Realm realm = realmManager.openLocalInstance()) { 
      realm.executeTransaction(r -> r.copyToRealmOrUpdate(helper)); 
     } 
    } 

技術のようにこれを使用することができますそこに魔法。

スレッドのRealmインスタンスを開いて、不要になったときに閉じることを忘れないでください。

+0

清算していただきありがとうございます!それで動作します:) –

+0

命の恩人に感謝!! –

関連する問題