2017-10-05 7 views
2

이는 수신 거래 Rx Observable 트랜잭션 내에서 영역 호출을 사용할 때 영역 스레딩 문제를 방지하려면 어떻게해야합니까?

RealmHelperRepo

이라고 뷰 모델이다는 HelperRepo 인터페이스

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

이 영역 호출이 만들어지는 RealmRepo의 클래스의 구현입니다.

@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 함수를 flatmap 대신 사용해야합니까? 스레딩 문제에 빠지지 않고 관찰 가능한 데이터를 저장하는 다른 방법이 있습니까? 도움!

답변

2

내가 여기에 뭔가가 있습니까?

Realm 인스턴스는 참조 카운트, 스레드 로컬 예를 나타낸다. 그것은 글로벌 일이 아니며, getInstance()에 의해 열린 다음 close()에 의해 닫히는 "로컬 인스턴스"입니다.

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

기술적처럼 이것을 사용할 수 있습니다 저기있는 마술.

스레드에 대한 영역 인스턴스를 열면 더 이상 필요하지 않을 때 닫는 것을 잊지 마십시오.

+0

설명해 주셔서 감사합니다. 그것에 작동합니다 :) –

+0

생명의 은인 감사합니다! –