2022년 9월 12일 월요일

Kotlin StateFlow , SharedFlow

Kotlin StateFlow , SharedFlow

StateFlow

Flow는편리한 API 집합이지만 일부 시나리오에 필요한 상태 관리를 제공하지 않습니다.

flow빌더로 구축된 콜드 데이터 스트림 과 달리 StateFlow는핫 데이터 스트림입니다.

핫 데이터 스트림 : 구독자가 있든 없든 이벤트가 전송되는 한 소비되므로 수신자가 수신할 수 있는지 여부는 이 시점에서 약간 우리의 Android Livedata와 비슷합니다

StateFlow현재 상태 업데이트와 수집기에 대한 새로운 상태 업데이트를 발행할 수 있는 상태 컨테이너 스타일의 관찰 가능한 데이터 스트림이며 값에 대한 업데이트는 모든 스트림 싱크에 새 값을 피드백합니다. 현재 상태 값은 속성 을 통해 value읽을 수도 있습니다 .

이러한 데이터 스트림에서 데이터를 수집해도 공급자 코드가 트리거되지 않습니다. StateFlow항상 활성 상태이고 메모리에 존재하며 가비지 수집 루트에 관련된 다른 참조가 없는 경우에만 가비지 수집에 적합합니다. 새로운 소비자가 데이터 스트림에서 데이터 수집을 시작하면 스트림의 가장 최근 상태와 모든 후속 상태를 수신합니다. 이 변경 사항은 LiveData비슷합니다.

StateFlow에는 두 가지 유형이 있습니다

  • MutableStateFlow
  • StateFlow

업데이트를 담당하는 클래스 MutableStateFlow는 공급자이고 StateFlow컬렉션의 모든 클래스는 소비자입니다.

class TestActivity : AppCompatActivity() {
    private val viewModel: TestFlowViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.test)
        lifecycleScope.launch {
            viewModel.state.collect {
                Log.d("carman","state : $it")
            }
        }
        viewModel.download()
    }
}

class TestFlowViewModel : ViewModel() {
    private val _state: MutableStateFlow<Int> = MutableStateFlow(0)
    val state: StateFlow<Int> get() = _state

    fun download() {
        for (state in 0..5) {
            viewModelScope.launch(Dispatchers.IO) {
                delay(200L * state)
                _state.value = state
            }
        }
    }
}
  • StateFlow를 라이브 데이터로 변환
val myLiveData = mViewModel.myFlow.asLiveData()

SharedFlow

SharedFlow는 관찰자를 등록할 수 있는 관찰자 패턴의 구체적인 구현입니다. 관찰자가 변경되면 관찰자에게 알림이 표시됩니다. 동시에 SharedFlow는 이전 변경 사항도 저장할 수 있습니다. 옵저버가 등록되어 있고 옵저버는 여전히 이전 변경 사항을 받을 수 있습니다.

SharedFlow에는 두 가지 유형이 있습니다

  • MutableSharedFlow
  • SharedFlow

StateFlow와의 차이점은 SharedFlow생성 시 초기 기본값을 설정할 수 있는 방법이 없다는 것입니다. 다만 처음 SharedFlow에는 3개의 선택적 구성 항목이 있습니다.

replay

기존 데이터를 신규 가입자에게 재생할 수 있는 신규 가입자에게 재전송할 값의 수입니다. 음수일 수 없으며 기본값은 0입니다.

extraBufferCapacity

replay버퍼 풀 수에 따라 버퍼 공간이 남아 있을 때 emit데이터 전송 호출이 일시 중단되지 않고 음수가 될 수 없으며 기본값은 0입니다.

onBufferOverflow

emit버퍼 오버플로에 대한 트리거 동작을 구성합니다. 기본값은BufferOverflow.SUSPEND버퍼 오버플로에서 일시 중단하는 것입니다. 또한BufferOverflow.DROP_OLDEST오버플로 시 버퍼에서 가장 오래된 값을 삭제하고 일시 중단하지 않고 버퍼에 새 값을 추가합니다.BufferOverflow.DROP_LATEST`버퍼 오버플로가 발생하면 현재 버퍼에 추가된 최신 값을 삭제하여 버퍼 내용을 일시 중단하지 않고 변경되지 않은 상태로 유지합니다.

class TestActivity : AppCompatActivity() {
    private val viewModel: TestFlowViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.test)
        lifecycleScope.launch {
            var index = 1
            launch {
                viewModel.state.collect {
                    Log.d("shared", "state1 : $it ")
                }
            }
           launch {
               delay(3000)
               viewModel.state.collect {
                   Log.d("shared", "state2 : $it")
               }
           }
        }
        viewModel.download()
    }
}

class TestFlowViewModel : ViewModel() {
    private val _state: MutableSharedFlow<Int> = MutableSharedFlow(2)
    val state: SharedFlow<Int> get() = _state
    fun download() {
        for (state in 0..5) {
            viewModelScope.launch(Dispatchers.IO) {
                delay(100L * state)
                _state.emit(state)
            }
        }
    }
}

위의 예제에서 3초후에 collect를 다시 요청할때, SharedFlow는 replay인수만큼 데이터를 요청받는 것을 알수 있습니다. (relay경주하듯이 데이터를 이어받는다고 생각하세요)
0-1-2-3-4-5-4-5

SharedFlow는 replay,buffer, queue, Cache 등 여러가지 설정 패턴을 가질수 있습니다.
상태 저장 섹션:

  • buffer: 크기가 항상 2의 거듭제곱일 때마다 생성되고 할당되는 버퍼링된 배열입니다.
  • replayIndex: 새로운 수집기(가입자)로부터 값을 얻기 위한 최소 인덱스. 즉, 업데이트 위치에 따라 신규 가입자에게 재방출되는 값의 개수의 위치 인덱스가 변경된다.
  • minCollectorIndex: 활성 수집기의 가장 작은 인덱스, 없는 경우 같음replayIndex
  • bufferSize: 버퍼 수
  • queueSize: 대기 중인 송신기의 수

상태를 계산하는 데 사용되는 부분:

  • head: 합계의 가장 작은 값인 replayIndex헤드 의 인덱스입니다 .minCollectorIndex
  • replaySize: 생성 시 결정되는 신규 가입자에게 재전송할 값의 수 replay.
  • totalSize: 총 개수, bufferSize합계 queueSize의 합계입니다.
  • bufferEndIndex: 버퍼 풀의 꼬리 인덱스
  • queueEndIndex: 송신기 큐의 꼬리 인덱스

캐시된 데이터 부분을 가져옵니다.

  • replayCache: 캐싱된 데이터 스냅샷, 컬렉션의 크기는 생성 시점에 결정됩니다. 즉 replay, 계산 부분에 사용되는 replaySize변수 입니다. 각 새 구독자는 먼저 캐시 스냅샷에서 값을 가져온 다음 새 트리거 값을 가져옵니다. MutableSharedFlowd 의 기능 으로 resetReplayCache재설정 할 수 있습니다

SharedFlow, StateFlow 는 메모리 누수방지를 위해서 repeatOnLifecycle(Lifecycle.State.STARTED) {} 안에서 수집하는게 좋습니다.

Flow를 SharedFlow로 변환

공식적으로 제공되는 shareIn 메소드를 사용하십시오:

val myFlow = flow<String> { }.shareIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(),
    replay = 0
)

started속성은 3개가 있습니다.

  • SharingStarted.WhileSubscribed(): 가입자가 있을 때 업스트림 공급자를 활성 상태로 유지합니다.
  • SharingStarted.Eagerly: 즉시 공급자를 시작합니다.
  • SharingStarted.Lazily: 첫 번째 구독자가 나타난 후 데이터 공유를 시작하고 데이터 스트림을 영원히 유지합니다.

SharedFlow 와 StateFlow 의 비교점

  • SharedFlow히스토리 캐시를 유지하고 새로운 데이터만 수신할 수 있는 존재로 일대다 이벤트 배포 시나리오에 적합한 선택입니다 .

  • StateFlow원래 LiveData위치와 일치하며 최신 데이터만 영원히 보유하므로 상태 업데이트 처리에 더 적합합니다 .

  • StateFlow는 replaySize=1인 sharedFlow로 초기값이 있어야 하며, 데이터가 업데이트될 때마다 기존 데이터와 비교하여 다를 경우에만 값을 업데이트합니다.

  • StateFlow 는 상태에 초점을 맞추고 ui는 항상 상태를 가지므로 StateFlow에는 초기 값이 있어야 하고 ui의 경우 만료된 상태는 의미가 없으므로 stateFlow는 항상 최신 데이터(liveData와 유사)를 업데이트하므로 replay = 1 이어어야 합니다. ui 상태를 최신 상태로 유지하십시오.

  • SharedFlow 는 이벤트에 초점을 맞추며 이벤트가 발생하면 큐로 보내지고 일시중단 또는 비중단, 캐싱 전략 등에 따라 이벤트가 수신자에게 전송됩니다. 특정 용도에서는 SharedFlow가 알림에 더 적합합니다. ui 인터페이스의 일부 이벤트 예를 들어 토스트 등은 viewModel과 저장소 간의 브리지로 데이터 전송에도 적합합니다.

LiveData, StateFlow, SharedFlow

이들 3개의 장점과 사용처를 구분해 봅시다

LiveData

MVVM설계에서 ViewModel과 가장편하게 쓸수있는 옵져버입니다.
안드로이드의 라이프사이클에 맞게 설계되어있고, 항상 최신의 데이터만 가지고 있기에 데이터를 기반으로 양방향 바인딩이 가능하여 UI 표시제어하는데 좋습니다.

  • LiveData UI를 업데이트하도록 설계된 수명 주기 인식, 관찰 가능한 데이터 홀더
  • LiveData매우 가볍고, 그 가치를 보여주기 위해 함께 ViewModel 과 궁합이 좋음
  • LiveData 단일 기능 에 초점을 맞추기 때문에 일부 메서드는 사용이 제한됩니다. 즉, 개발자가 올바른 방식으로 코딩하도록 설계되었습니다(예: 관찰자는 기본 스레드에서만 콜백하므로 개발자는 하위 스레드에서 UI를 업데이트해야 합니다.)

StageFlow

LiveData 와 아래와 같은 기능이 유사합니다.

  • “읽기 및 쓰기 가능” 및 “읽기 전용” 버전( StateFlow, MutableStateFlow) 으로 사용 가능
  • 보유값이 독립적입니다.
  • 여러 관찰자가 공유할 수 있습니다(따라서 공유 데이터 스트림).
  • 활성 관찰자 수에 관계없이 구독자에게 최신 값만 재생산합니다.
  • DataBinding지원
    아래와 같은 점이 다릅니다.
  • 초기설정값이 필요합니다.
  • null안전합니다.

SharedFlow

아래같은 점이 다릅니다.

  • MutableSharedFlow시작 값 없음
  • SharedFlow과거 데이터를 유지할 수 있습니다.
  • MutableSharedFlow값을 내보내려면 메서드를 호출해야 합니다.

0 comments:

댓글 쓰기