2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
प्रथमयोः लेखयोः परिचयः कृतः यत् Flow किम् अस्ति, तस्य उपयोगः कथं करणीयः, तत्सम्बद्धाः संचालकाः उन्नतयः च अग्रिमे लेखे मुख्यतया वास्तविकपरियोजनासु Flow इत्यस्य उपयोगस्य परिचयः कृतः अस्ति ।
Flow इत्यस्य वास्तविक-अनुप्रयोग-परिदृश्यानां परिचयात् पूर्वं प्रथमं Flow इत्यस्य प्रथमे लेखे प्रवर्तितस्य टाइमर-उदाहरणस्य समीक्षां कुर्मः वयं ViewModel इत्यस्मिन् timeFlow-दत्तांशप्रवाहं परिभाषितवन्तः ।
class MainViewModel : ViewModel() {
val timeFlow = flow {
var time = 0
while (true) {
emit(time)
delay(1000)
time++
}
}
ततः Activity इत्यस्मिन् पूर्वं परिभाषितं data stream प्राप्नुवन्तु ।
lifecycleOwner.lifecycleScope.launch {
viewModel.timeFlow.collect { time ->
times = time
Log.d("ddup", "update UI $times")
}
}
वास्तविकं प्रभावं द्रष्टुं चालयामि :
किं भवन्तः अवलोकितवन्तः यत् यदा App पृष्ठभूमिं प्रति गच्छति तदा अपि log मुद्रयति एतत् संसाधनानाम् अपव्ययः नास्ति ।
lifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.timeFlow.collect { time ->
times = time
Log.d("ddup", "update UI $times")
}
}
launch तः launchWhenStarted यावत् coroutine इत्यस्य आरम्भस्य विधिं परिवर्तयामः, प्रभावं द्रष्टुं पुनः चालयामः च:
वयं द्रष्टुं शक्नुमः यत् यदा HOME बटनं क्लिक् कृत्वा पृष्ठभूमिं प्रति प्रत्यागच्छति तदा परिवर्तनं प्रभावी अभवत् इति द्रष्टुं शक्यते, परन्तु किं धारा रद्दीकृता अस्ति? a look:
अग्रभूमिं प्रति स्विच् कृत्वा वयं द्रष्टुं शक्नुमः यत् काउण्टरः 0 तः आरभ्यते, अतः वस्तुतः सः स्वागतं रद्दं न करोति, केवलं पृष्ठभूमितः दत्तांशं प्राप्तुं विरामं करोति The Flow pipeline अद्यापि पूर्वदत्तांशं धारयति एपिआइ परित्यक्तम् अस्ति तस्य स्थाने Google RepeatOnLifecycle अधिकं अनुशंसितम्, तस्य च पुरातनदत्तांशं पाइपलाइन् मध्ये धारयितुं समस्या नास्ति ।
तत्सम्बद्धं कोडं परिवर्तयितुं प्रयतेम :
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.timeFlow.collect { time ->
times = time
Log.d("ddup", "update UI $times")
}
}
}
प्रभावं द्रष्टुं पुनः चालयन्तु :
वयं द्रष्टुं शक्नुमः यत् पृष्ठभूमितः अग्रभूमिं प्रति स्विच् करणसमये पुनः 0 तः दत्तांशः आरभ्यते, यस्य अर्थः अस्ति यत् पृष्ठभूमिं प्रति स्विच् करणसमये Flow कार्यं रद्दं करोति तथा च सर्वाणि मूलदत्तांशं स्वच्छं भवति
वयं Flow इत्यस्य उपयोगं कुर्मः, repeatOnLifecycle इत्यस्य माध्यमेन च अस्माकं कार्यक्रमस्य सुरक्षां अधिकतया सुनिश्चितं कर्तुं शक्नुमः ।
पूर्वपरिचयः सर्वे Flow cold flow इत्यस्य उदाहरणानि सन्ति तदनन्तरं वयं hot flow इत्यस्य केचन सामान्याः अनुप्रयोगपरिदृश्याः परिचययिष्यामः ।
अद्यापि पूर्वस्य समयनिर्धारकस्य उदाहरणस्य उपयोगेन यदि पटलः क्षैतिज-लम्ब-पर्दे मध्ये स्विच् भवति तर्हि किं भविष्यति?
वयं पश्यामः यत् क्षैतिज-ऊर्ध्वाधर-पर्देषु स्विच् कृत्वा Activity पुनः निर्मितं भवति, पुनः निर्मितस्य अनन्तरं timeFlow पुनः संग्रहितः भविष्यति, शीतलप्रवाहः पुनः संग्रहितः भविष्यति, पुनः निष्पादितः भविष्यति, ततः समयनिर्धारकः आरभ्यते counting from 0. बहुवारं वयं क्षैतिज-लम्ब-पर्दे मध्ये स्विच् कर्तुम् इच्छामः अस्मिन् समये वयं आशास्महे यत् पृष्ठस्य स्थितिः अपरिवर्तिता एव तिष्ठति, न्यूनातिन्यूनं निश्चितकालस्य अन्तः अत्र वयं शीतप्रवाहं उष्णं प्रति परिवर्तयामः प्रवाहं कृत्वा प्रयतस्व : १.
val hotFlow =
timeFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
0
)
```
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.hotFlow.collect { time ->
times = time
Log.d("ddup", "update UI $times")
}
}
}
```
अत्र वयं stateIn इत्यस्मिन् त्रयाणां पैरामीटर्-मध्ये ध्यानं दद्मः प्रथमः coroutine इत्यस्य व्याप्तिः अस्ति, द्वितीयः प्रवाहस्य कार्य-स्थितिं निर्वाहयितुम् अधिकतमः प्रभावी समयः अस्ति यदि सः प्रवाहं अतिक्रमयति तर्हि अन्तिमः कार्यं स्थगयति पैरामीटर् प्रारम्भिकं मूल्यम् अस्ति ।
प्रभावं द्रष्टुं पुनः चालयन्तु :
अत्र वयं क्षैतिज-ऊर्ध्वाधर-पर्देषु स्विच् कृत्वा मुद्रितं लॉग् द्रष्टुं शक्नुमः ।
वयं उपरि शीतप्रवाहं कथं उष्णप्रवाहं परिवर्तयितुं शक्नुमः इति परिचयं कृतवन्तः यत् stateFlow LiveData इत्यस्य स्थाने कथं भवति इति परिचयः अस्ति ।
private val _stateFlow = MutableStateFlow(0)
val stateFlow = _stateFlow.asStateFlow()
fun startTimer() {
val timer = Timer()
timer.scheduleAtFixedRate(object :TimerTask() {
override fun run() {
_stateFlow.value += 1
}
},0,1000)
}
```
viewModel.startTimer()
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.stateFlow.collect { time ->
times = time
Log.d("ddup", "update UI $times")
}
}
}
```
वयं StateFlow hot flow परिभाषयामः, ततः startTimer() मेथड् इत्यस्य माध्यमेन stateFlow मूल्यं परिवर्तयामः, LiveData setData इत्यस्य सदृशं यदा बटनं क्लिक् भवति तदा StateFlow मूल्यं परिवर्तयितुं आरभत तथा च तत्सम्बद्धस्य प्रवाहस्य मूल्यानि संग्रहयामः, यथा दत्तांशपरिवर्तनस्य निरीक्षणार्थं LiveData Observe पद्धतिः ।
वास्तविकं धावनप्रभावं पश्यामः :
अस्मिन् क्षणे वयं StateFlow इत्यस्य मूलभूतं उपयोगं प्रवर्तयामः, अधुना SharedFlow इत्यस्य परिचयं करिष्यामः ।
SharedFlow इत्यस्य अवगमनाय वयं प्रथमं चिपचिपानां घटनानां अवधारणां जानीमः अक्षरशः यदा कश्चन पर्यवेक्षकः दत्तांशस्रोतस्य सदस्यतां लभते तदा यदि दत्तांशस्रोते पूर्वमेव नवीनतमदत्तांशः अस्ति तर्हि दत्तांशः तत्क्षणमेव पर्यवेक्षकस्य समीपं धक्कायते उपर्युक्तव्याख्यानात् न्याय्यं चेत्, LiveData अस्य चिपचिपा लक्षणस्य अनुरूपं भवति StateFlow इत्यस्य विषये किम्? सत्यापनार्थं सरलं प्रदर्शनं लिखामः :
class MainViewModel : ViewModel() {
private val _clickCountFlow = MutableStateFlow(0)
val clickCountFlow = _clickCountFlow.asStateFlow()
fun increaseClickCount() {
_clickCountFlow.value += 1
}
}
//MainActivity
```
val tv = findViewById<TextView>(R.id.tv_content)
val btn = findViewById<Button>(R.id.btn)
btn.setOnClickListener {
viewModel.increaseClickCount()
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.clickCountFlow.collect { time ->
tv.text = time.toString()
Log.d("ddup", "update UI $time")
}
}
}
```
वयं प्रथमं MainViewModel मध्ये clickCountFlow परिभाषयामः, ततः Activity मध्ये, Button इत्यत्र क्लिक् कृत्वा clickCountFlow data परिवर्तयामः, ततः clickCountFlow प्राप्य पाठे data प्रदर्शयामः
रनिंग् इफेक्ट् अवलोकयामः :
वयं पश्यामः यत् क्षैतिज-ऊर्ध्वाधर-पर्देषु स्विच् करणसमये Activity पुनः निर्मितं भवति तथा च clickCountFlow पुनः संगृहीतं भवति, यत् सूचयति यत् StateFlow चिपचिपाः इति सूचयति, परन्तु अस्तु अन्यत् उदाहरणं पश्यन्तु।
//MainViewModel
private val _loginFlow = MutableStateFlow("")
val loginFlow = _loginFlow.asStateFlow()
fun startLogin() {
// Handle login logic here.
_loginFlow.value = "Login Success"
}
//MainActivity
```
val tv = findViewById<TextView>(R.id.tv_content)
val btn = findViewById<Button>(R.id.btn)
btn.setOnClickListener {
viewModel.startLogin()
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.loginFlow.collect {
if (it.isNotBlank()) {
Toast.makeText(this@MainActivity2, it, Toast.LENGTH_LONG).show()
}
}
}
}
```
उपरिष्टाद् कोडः वस्तुतः क्लिक्-प्रवेशस्य अनुकरणं करोति, ततः लॉगिन् सफलम् इति प्रेरयति ।
किं भवन्तः दृष्टवन्तः यत् क्षैतिज-ऊर्ध्वाधर-पर्देषु स्विच-करणानन्तरं पुनः सफलः प्रवेश-प्रोम्प्ट्-उत्पादः भवति? SharedFlow कृत्वा तस्य प्रयासं कुर्वन्तु:
private val _loginFlow = MutableSharedFlow<String>()
val loginFlow = _loginFlow.asSharedFlow()
fun startLogin() {
// Handle login logic here.
viewModelScope.launch {
_loginFlow.emit("Login Success")
}
}
वयं StateFlow इत्येतत् SharedFlow इति परिवर्तयामः यत् SharedFlow इत्यस्य प्रारम्भिकमूल्यं आवश्यकं नास्ति ।
अत्र वयं द्रष्टुं शक्नुमः यत् SharedFlow इत्यस्य उपयोगे एषा चिपचिपाहटसमस्या न भवति वस्तुतः SharedFlow इत्यस्य बहवः पैरामीटर्स् सन्ति येषां विन्यस्तं कर्तुं शक्यते ।
public fun <T> MutableSharedFlow(
// 每个新的订阅者订阅时收到的回放的数目,默认0
replay: Int = 0,
// 除了replay数目之外,缓存的容量,默认0
extraBufferCapacity: Int = 0,
// 缓存区溢出时的策略,默认为挂起。只有当至少有一个订阅者时,onBufferOverflow才会生效。当无订阅者时,只有最近replay数目的值会保存,并且onBufferOverflow无效。
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
)
सर्वेषां आविष्कारं प्रतीक्षमाणाः SharedFlow इत्यस्य अधिकाः उपयोगाः सन्ति, परन्तु अहम् अत्र विस्तरेण न गमिष्यामि ।
पूर्वं वयं मूलभूतं शीतप्रवाहं उष्णप्रवाहस्य परिचयं कृतवन्तः, तथैव StateFlow तथा SharedFlow इत्येतयोः सामान्यप्रयोगं प्रयोज्यपरिदृश्यानि च तदनन्तरं प्रवाहस्य अन्यसामान्यप्रयोगपरिदृश्यानि द्रष्टुं अनेकव्यावहारिकउदाहरणेषु केन्द्रीभविष्यामः
वयं प्रायः किञ्चित् जटिलं समयग्राहकं तर्कं कुर्मः, उप-धागे संसाधयामः, ततः UI प्रदर्शयितुं मुख्य-धागं प्रति स्विच् कुर्मः Flow अपि थ्रेड्-स्विचिंग् समर्थयति, तथा च flowOn प्रक्रियां कर्तुं तत्सम्बद्धे उप-धागे पूर्व-क्रियाः स्थापयितुं शक्नोति .
वयं पठितस्थानीयं कार्यान्वयामःAssets
निर्देशिकायाः अधःperson.json
सञ्चिकां कृत्वा तस्य विश्लेषणं कुर्वन्तु, .json
सञ्चिकायाः विषयवस्तु : १.
{
"name": "ddup",
"age": 101,
"interest": "earn money..."
}
ततः सञ्चिकां विश्लेषणं कुर्वन्तु :
fun getAssetJsonInfo(context: Context, fileName: String): String {
val strBuilder = StringBuilder()
var input: InputStream? = null
var inputReader: InputStreamReader? = null
var reader: BufferedReader? = null
try {
input = context.assets.open(fileName, AssetManager.ACCESS_BUFFER)
inputReader = InputStreamReader(input, StandardCharsets.UTF_8)
reader = BufferedReader(inputReader)
var line: String?
while ((reader.readLine().also { line = it }) != null) {
strBuilder.append(line)
}
} catch (ex: Exception) {
ex.printStackTrace()
} finally {
try {
input?.close()
inputReader?.close()
reader?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return strBuilder.toString()
}
प्रवाहः सञ्चिकाः पठति : १.
/**
* 通过Flow方式,获取本地文件
*/
private fun getFileInfo() {
lifecycleScope.launch {
flow {
//解析本地json文件,并生成对应字符串
val configStr = getAssetJsonInfo(this@MainActivity2, "person.json")
//最后将得到的实体类发送到下游
emit(configStr)
}
.map { json ->
Gson().fromJson(json, PersonModel::class.java) //通过Gson将字符串转为实体类
}
.flowOn(Dispatchers.IO) //在flowOn之上的所有操作都是在IO线程中进行的
.onStart { Log.d("ddup", "onStart") }
.filterNotNull()
.onCompletion { Log.d("ddup", "onCompletion") }
.catch { ex -> Log.d("ddup", "catch:${ex.message}") }
.collect {
Log.d("ddup", "collect parse result:$it")
}
}
}
अन्तिममुद्रणवृत्तान्तः : १.
2024-07-09 22:00:34.006 12251-12251 ddup com.ddup.flowtest D onStart 2024-07-09 22:00:34.018 12251-12251 ddup com.ddup.flowtest D collect parse result:PersonModel(name=ddup, age=101, interest=earn money...) 2024-07-09 22:00:34.019 12251-12251 ddup com.ddup.flowtest D onCompletion
वयं प्रायः अन्यस्य अनुरोधस्य परिणामेषु निर्भराः अन्तरफलक-अनुरोधाः प्राप्नुमः, ये तथाकथिताः नेस्टेड्-अनुरोधाः सन्ति यदि अत्यधिकाः नेस्टेड्-अनुरोधाः सन्ति तर्हि वयं तथैव आवश्यकतां कार्यान्वितुं FLow-इत्यस्य उपयोगं कुर्मः ।
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
//将两个flow串联起来 先搜索目的地,然后到达目的地
viewModel.getTokenFlows()
.flatMapConcat {
//第二个flow依赖第一个的结果
viewModel.getUserFlows(it)
}.collect {
tv.text = it ?: "error"
}
}
}
बहुविध-अन्तरफलकात् दत्तांशं संयोजयितुं किं परिदृश्यम् अस्ति उदाहरणार्थं, वयं बहु-अन्तरफलकान् अनुरोधयामः, ततः तेषां परिणामान् एकरूपेण प्रदर्शयितुं संयोजयामः अथवा अन्यस्य अन्तरफलकस्य अनुरोध-मापदण्डरूपेण उपयोगं कुर्मः, एतत् कथं कार्यान्वितम्
प्रथमं एकैकं याचयित्वा ततः तान् विलीनं कृत्वा;
द्वितीयः प्रकारः युगपत् अनुरोधः, ततः सर्वाणि अनुरोधाः विलीनीकरणम् ।
स्पष्टतया द्वितीयः प्रभावः अधिकः कार्यकुशलः अस्ति ।
//分别请求电费、水费、网费,Flow之间是并行关系
suspend fun requestElectricCost(): Flow<SpendModel> =
flow {
delay(500)
emit(SpendModel("电费", 10f, 500))
}.flowOn(Dispatchers.IO)
suspend fun requestWaterCost(): Flow<SpendModel> =
flow {
delay(1000)
emit(SpendModel("水费", 20f, 1000))
}.flowOn(Dispatchers.IO)
suspend fun requestInternetCost(): Flow<SpendModel> =
flow {
delay(2000)
emit(SpendModel("网费", 30f, 2000))
}.flowOn(Dispatchers.IO)
प्रथमं वयं ViewModel मध्ये अनेकाः संजाल-अनुरोधाः अनुकरणं कृत्वा परिभाषितवन्तः, ततः अनुरोधाः विलीनवन्तः:
lifecycleScope.launch {
val electricFlow = viewModel.requestElectricCost()
val waterFlow = viewModel.requestWaterCost()
val internetFlow = viewModel.requestInternetCost()
val builder = StringBuilder()
var totalCost = 0f
val startTime = System.currentTimeMillis()
//NOTE:注意这里可以多个zip操作符来合并Flow,且多个Flow之间是并行关系
electricFlow.zip(waterFlow) { electric, water ->
totalCost = electric.cost + water.cost
builder.append("${electric.info()},n").append("${water.info()},n")
}.zip(internetFlow) { two, internet ->
totalCost += internet.cost
two.append(internet.info()).append(",nn总花费:$totalCost")
}.collect {
tv.text = it.append(",总耗时:${System.currentTimeMillis() - startTime} ms")
Log.d(
"ddup",
"${it.append(",总耗时:${System.currentTimeMillis() - startTime} ms")}"
)
}
}
संचालन परिणामः : १.
वयं पश्यामः यत् व्ययितस्य कुलसमयः मूलतः दीर्घतमस्य अनुरोधसमयस्य समानः एव भवति ।
बहुविधःFlow
एकस्मिन् स्थापयितुं न शक्यतेlifecycleScope.launch
अन्तः गच्छतुcollect{}
, यतः प्रविशन्collect{}
अनन्तपाशस्य समकक्षं, कोडस्य अग्रिमपङ्क्तिः कदापि निष्पादिता न भविष्यति यदि भवान् केवलं a लिखितुम् इच्छति;lifecycleScope.launch{}
अन्तः गच्छतु, अन्तःतः पुनः चालू कर्तुं शक्नुथlaunch{}
उपकोरूटिनः निष्पादितः भवति ।
त्रुटि उदाहरणम् : १.
lifecycleScope.launch {
flow1
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect {}
flow2
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect {}
}
लेखनस्य सम्यक् मार्गः : १.
lifecycleScope.launch {
launch {
flow1
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect {}
}
launch {
flow2
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect {}
}
}
Flow इत्यस्य जीवनचक्रात् वयं संसाधनानाम् अपव्ययस्य परिहाराय प्रवाहस्य सम्यक् उपयोगमुद्रां प्रवर्तयामः, साधारणशीतप्रवाहस्य उष्णप्रवाहे परिवर्तनं यावत्, LiveData इत्यस्य स्थाने StateFlow इत्यस्मै, तस्य चिपचिपाहटसमस्यां च, ततः माध्यमेन चिपचिपाहटसमस्यायाः समाधानं कृतवन्तः SharedFlow, ततः सामान्यं Application परिदृश्यं प्रति, अन्ते च Flow इत्यस्य उपयोगाय सावधानताः, मूलतः Flow इत्यस्य अधिकांशं विशेषतां अनुप्रयोगपरिदृश्यं च आच्छादयन्ति एषः Flow शिक्षणस्य अन्तिमः अध्यायः अपि अस्ति
न सुकरं सृज्यते, परन्तु तस्य रोचनं कष्टप्रदम्प्रोत्साहनार्थं पसन्दं कुर्वन्तु, संग्रहयन्तु, टिप्पणीं च कुर्वन्तु。
सन्दर्भ लेख
Kotlin Flow प्रतिक्रियाशीलप्रोग्रामिंग्, StateFlow तथा SharedFlow इति