2017-10-13 95 views
10

최근 Google에서 출시 한 새로운 Android Architecture Components, 특히 ViewModel 라이프 사이클 인식 클래스를 MVVM 아키텍처 및 LiveData와 함께 사용하기로 결정했습니다.MVVM 패턴 및 startActivity

단일 액티비티 또는 단일 프래그먼트를 다루는 한 모든 것이 정상입니다.

그러나 활동 전환을 처리하는 좋은 해결책을 찾을 수 없습니다. 간단한 예를 들어 Activity A에 Activity B를 시작하는 버튼이 있다고 가정 해보십시오.

startActivity()는 어디에서 처리됩니까?

MVVM 패턴에 따라 clickListener의 로직은 ViewModel에 있어야합니다. 그러나 우리는 거기서 Activity에 대한 참조를 피하기를 원합니다. 따라서 컨텍스트를 ViewModel에 전달하는 것은 옵션이 아닙니다.

나는 "OK"로 보이는 몇 가지 옵션을 좁혔지만 "여기에 어떻게 할 것인가"에 대한 적절한 답을 찾을 수 없었습니다.

옵션 1 : 값이 가능한 라우팅 (ACTIVITY_B, ACTIVITY_C)에 매핑 된 값으로 ViewModel에 열거 형을 지정하십시오. 이걸 LiveData와 연결하십시오. 액티비티는이 LiveData를 관찰 할 것이고, ViewModel이 ACTIVITY_C를 시작해야한다고 결정할 때 postValue (ACTIVITY_C) 일뿐입니다. 액티비티는 startActivity()를 정상적으로 호출 할 수 있습니다.

옵션 2 : 일반 인터페이스 패턴. 옵션 1과 같은 원리이지만, 액티비티는 인터페이스를 구현합니다. 나는 이것과 조금 더 많은 결합을 느낀다.

옵션 3 : 오토와 같은 메시징 옵션. ViewModel은 브로드 캐스트를 전송하고, Activity는이를 선택하여 시작해야하는 것을 시작합니다. 이 솔루션의 문제점은 기본적으로 ViewModel 내에 해당 브로드 캐스트의 등록/등록 취소를 지정해야한다는 것입니다. 그래서 도움이되지 않습니다.

옵션 4 : 임의의 활동에 관련 라우팅을 발송하기 위해 호출 될 수있는 큰 라우팅 클래스를 단독으로 또는 유사한 것으로 어딘가에 가지고 있습니다. 결국 인터페이스를 통해? 그래서 모든 활동 (또는 BaseActivity)는 앱이 그건 그래서 조각/많은 활동 (라우팅 클래스는 엄청난 될 것 때문에)

을 가지고 시작할 때이 방법은 단지 나에게 조금 걱정

IRouting { void requestLaunchActivity(ACTIVITY_B); } 

을 구현하는 것이 그것. 그게 내 질문이다. 어떻게 처리합니까? 내가 생각하지 못했던 옵션을 사용합니까? 어떤 옵션이 가장 관련성이 높고 그 이유는 무엇이라고 생각하십니까? Google의 권장 접근 방식은 무엇입니까?

PS : - Android ViewModel call Activity methods 2 - 당신은 AAC를 사용하여 시작하는 것이 가장 큰 How to start an activity from a plain non-activity java class?

+1

감사합니다. 기꺼이 도와 드리겠습니다 :-) –

답변

5

NSimon, 나에게 어디 일을하지 않았다 링크.

그 전에 나는 aac's-github에 issue이라고 썼습니다.

몇 가지 방법이 있습니다.

하나 개의 솔루션은 활동의 컨텍스트를 보유하고있는 navigationController에

WeakReference을 사용하는 것입니다. 이것은 ViewModel 내에서 컨텍스트 바운드 내용을 처리하기 위해 일반적으로 사용되는 패턴입니다.

나는 이것을 여러 이유로 거부합니다. 첫 번째는 일반적으로 컨텍스트 누출을 수정하는 NavigationController에 대한 참조를 유지해야한다는 것을 의미하지만 아키텍처를 전혀 해결하지 못합니다.

(내 의견으로는) 수명주기를 인식하고 원하는 모든 작업을 수행 할 수있는 LiveData를 사용하는 것이 가장 좋습니다.

예 : 그 후

class YourVm : ViewModel() { 

    val uiEventLiveData = SingleLiveData<Pair<YourModel, Int>>() 
    fun onClick(item: YourModel) { 
     uiEventLiveData.value = item to 3 // can be predefined values 
    } 
} 

당신은 변화를보기 안에들을 수 있습니다. 항상 나쁜 행동에 이르게 새 관찰자의 최신 결과를 방출합니다 다른 때문

class YourFragmentOrActivity { 
    //assign your vm whatever 
    override fun onActivityCreated(savedInstanceState: Bundle?) { 
     var context = this 
     yourVm.uiEventLiveData.observe(this, Observer { 
      when (it?.second) { 
       1 -> { context.startActivity(...) } 
       2 -> { .. } 
      } 

     }) 
    } 
} 

는 수정 MutableLiveData을 사용하는 필자주의하십시오. 예를 들어, 활동을 변경하고 되돌아 가면 루프로 끝납니다.

class SingleLiveData<T> : MutableLiveData<T>() { 

    private val mPending = AtomicBoolean(false) 

    @MainThread 
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) { 

     if (hasActiveObservers()) { 
      Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") 
     } 

     // Observe the internal MutableLiveData 
     super.observe(owner, Observer { t -> 
      if (mPending.compareAndSet(true, false)) { 
       observer.onChanged(t) 
      } 
     }) 
    } 

    @MainThread 
    override fun setValue(t: T?) { 
     mPending.set(true) 
     super.setValue(t) 
    } 

    /** 
    * Used for cases where T is Void, to make calls cleaner. 
    */ 
    @MainThread 
    fun call() { 
     value = null 
    } 

    companion object { 
     private val TAG = "SingleLiveData" 
    } 
} 

왜있는 WeakReferences, 인터페이스, 또는 다른 솔루션을 사용하여 그 시도 더 나은 무엇입니까?

이 이벤트는 비즈니스 논리가있는 UI 논리를 분리했기 때문에 발생합니다. 여러 관측자가있을 수도 있습니다. 그것은 라이프 사이클을 염려합니다. 그것은 아무것도 누설하지 않습니다.

PublishSubject를 사용하여 LiveData 대신 RxJava를 사용하여 해결할 수도 있습니다. (addToRxKotlin이 필요합니다.)

onStop()에서 구독을 유출하지 않도록주의하십시오.

class YourVm : ViewModel() { 
    var subject : PublishSubject<YourItem> = PublishSubject.create(); 
} 

class YourFragmentOrActivityOrWhatever { 
    var composite = CompositeDisposable() 
    onStart() { 
     YourVm.subject 
      .subscribe({ Log.d("...", "Event emitted $it") }, { error("Error occured $it") }) 
       .addTo(compositeDisposable)   
     } 
     onStop() { 
     compositeDisposable.clear() 
     } 
    } 

또한 ViewModel이 Activity 또는 Fragment에 바인딩되어 있는지 확인하십시오. 이 경우 "Livecycle-Awareness"가 깨질 수 있으므로 여러 활동간에 ViewModel을 공유 할 수 없습니다.

room과 같은 데이터베이스를 사용하여 데이터를 유지하거나 필지를 사용하여 데이터를 공유해야하는 경우.

+0

매우 상세한 답변을 보내 주셔서 감사합니다. 나는 또한 LiveData 접근법에 기대고 있었지만 LiveData를 조정할 생각은하지 않았습니다. 전반적으로 모든 것이 매우 "해킹 된 것"으로 보이며 이런 방식으로 처리하는 것이 거의 안 좋은 느낌입니다. 어쨌든 감사합니다 ! (편집 : 20 시간 만에 현상금 유효성 확인 가능) – NSimon

+1

아니, 해커가 아니야. 관찰자 패턴이 어떻게 작용하는지. 클릭이 발생하면 ViewModel로 이동하고 View가 있으면 (보장됨) 데이터를 처리합니다. 그것도 패치 :) 그건 어떻게 작동합니다. 클릭하면 그냥 샘플, 그것은 모든 "데이터 입력"수 있습니다. –