{"id":634,"date":"2025-07-15T23:12:26","date_gmt":"2025-07-15T23:12:26","guid":{"rendered":"https:\/\/boochlin.com\/?p=634"},"modified":"2025-08-27T15:04:09","modified_gmt":"2025-08-27T15:04:09","slug":"viewmodelscope-vs-remembercoroutinescope-%e5%85%b6%e5%af%a6%e6%b2%92%e5%95%a5%e5%a5%bd-versus-%ef%bc%8c%e5%b0%b1%e6%98%af-soc-%e8%81%b7%e8%b2%ac%e5%88%86%e9%9b%a2","status":"publish","type":"post","link":"https:\/\/boochlin.com\/?p=634","title":{"rendered":"viewModelScope vs rememberCoroutineScope \u5176\u5be6\u6c92\u5565\u597d versus \uff0c\u5c31\u662f SOC \u8077\u8cac\u5206\u96e2"},"content":{"rendered":"<ul>\n<li>viewModelScope\uff1a\u8207 <strong>ViewModel<\/strong> \u7684\u751f\u547d\u9031\u671f\u7d81\u5b9a\uff0c\u53ea\u80fd\u7528\u5728<strong>\u8cc7\u6599\/\u908f\u8f2f\u5c64<\/strong>\u3002<\/li>\n<li>rememberCoroutineScope\uff1a\u8207 <strong>Composable \u756b\u9762\u5143\u4ef6<\/strong>\u7684\u751f\u547d\u9031\u671f\u7d81\u5b9a\uff0c\u53ea\u80fd\u7528\u5728 <strong>UI \u5c64<\/strong>\u3002<\/li>\n<\/ul>\n<p>\u6c38\u9060\u4e0d\u8981\u5728 rememberCoroutineScope \u4e2d\u8655\u7406\u696d\u52d9\u908f\u8f2f\uff0c\u4e5f\u6c38\u9060\u4e0d\u8981\u5728 viewModelScope \u4e2d\u76f4\u63a5\u63a7\u5236\u9700\u8981 Composable \u4e0a\u4e0b\u6587\u7684 UI \u5143\u4ef6<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<h3 class=\"wp-block-heading\">\u4f55\u6642\u8a72\u7528\u54ea\u500b\uff1f<\/h3>\n<p>\u4f60\u53ef\u4ee5\u554f\u81ea\u5df1\u4e00\u500b\u554f\u984c\uff1a<\/p>\n<p>\u300c\u7576\u624b\u6a5f\u87a2\u5e55\u65cb\u8f49\u6642\uff0c\u9019\u500b\u975e\u540c\u6b65\u4efb\u52d9\u61c9\u8a72\u8981\u7e7c\u7e8c\u57f7\u884c\uff0c\u9084\u662f\u61c9\u8a72\u8981\u53d6\u6d88\u91cd\u4f86\uff1f\u300d<\/p>\n<ul>\n<li><strong>\u5982\u679c\u7b54\u6848\u662f\u300c\u61c9\u8a72\u7e7c\u7e8c\u57f7\u884c\u300d<\/strong>: \u90a3\u5c31\u7528 viewModelScope\u3002\n<ul>\n<li><strong>\u60c5\u5883<\/strong>: \u4f60\u6b63\u5728\u5f9e\u7db2\u8def\u4e0b\u8f09\u4e00\u500b\u4f7f\u7528\u8005\u982d\u50cf\u3002\u4f7f\u7528\u8005\u53ea\u662f\u65cb\u8f49\u4e00\u4e0b\u624b\u6a5f\uff0c\u4f60\u4e0d\u5e0c\u671b\u4e0b\u8f09\u4e2d\u65b7\uff0c\u800c\u662f\u5e0c\u671b\u5b83\u7e7c\u7e8c\u5728\u80cc\u666f\u5b8c\u6210\uff0c\u7136\u5f8c\u986f\u793a\u5728\u65b0\u65b9\u5411\u7684\u756b\u9762\u4e0a\u3002\u9019\u662f\u8cc7\u6599\u5c64\u7684\u4efb\u52d9\uff0c\u8207 ViewModel \u7684\u751f\u547d\u9031\u671f\u4e00\u81f4\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre class=\"wp-block-code\"><code>\/\/ \u5728 ViewModel \u4e2d\nclass ProfileViewModel : ViewModel() {\n    fun fetchAvatar() {\n        viewModelScope.launch {\n            \/\/ \u5373\u4f7f\u87a2\u5e55\u65cb\u8f49\uff0c\u9019\u500b\u4e0b\u8f09\u4efb\u52d9\u4e5f\u4e0d\u6703\u4e2d\u65b7\n            val avatar = repository.downloadAvatar()\n            _uiState.update { it.copy(avatar = avatar) }\n        }\n    }\n}<\/code><\/pre>\n<ul>\n<li><strong>\u5982\u679c\u7b54\u6848\u662f\u300c\u61c9\u8a72\u53d6\u6d88\u91cd\u4f86\u300d\u6216\u300c\u61c9\u8a72\u505c\u6b62\u300d<\/strong>: \u90a3\u5c31\u7528 rememberCoroutineScope\u3002\n<ul>\n<li><strong>\u60c5\u5883<\/strong>: \u7576\u67d0\u500b\u72c0\u614b\u8b8a\u70ba true \u6642\uff0c\u4f60\u9700\u8981\u986f\u793a\u4e00\u500b Snackbar \u63d0\u793a\u300c\u8a0a\u606f\u5df2\u50b3\u9001\u300d\u3002\u5982\u679c\u4f7f\u7528\u8005\u5728\u9019\u6642\u65cb\u8f49\u4e86\u87a2\u5e55\uff0c\u820a\u7684 Snackbar \u5c31\u6c92\u6709\u610f\u7fa9\u4e86\uff0c\u4f60\u53ef\u80fd\u6703\u60f3\u5728\u65b0\u756b\u9762\u4e0a\u6839\u64da\u65b0\u7684\u72c0\u614b\u6c7a\u5b9a\u662f\u5426\u91cd\u65b0\u986f\u793a\u3002\u9019\u500b\u4efb\u52d9\u5b8c\u5168\u4f9d\u9644\u65bc\u7576\u524d\u7684 UI\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre class=\"wp-block-code\"><code>\/\/ \u5728 Composable \u4e2d\n@Composable\nfun MessageSender(snackbarHostState: SnackbarHostState) {\n    \/\/ \u53d6\u5f97\u4e00\u500b\u8207 Composable \u751f\u547d\u9031\u671f\u7d81\u5b9a\u7684 scope\n    val scope = rememberCoroutineScope()\n\n    Button(\n        onClick = {\n            \/\/ \u5728\u9ede\u64ca\u4e8b\u4ef6\u9019\u7a2e callback \u4e2d\uff0c\u624b\u52d5\u555f\u52d5\u5354\u7a0b\n            scope.launch {\n                \/\/ \u9019\u500b\u4efb\u52d9\u8207 UI \u76f8\u95dc\uff0c\u5982\u679c\u756b\u9762\u6d88\u5931\uff0c\u4efb\u52d9\u4e5f\u8a72\u53d6\u6d88\n                snackbarHostState.showSnackbar(\"\u8a0a\u606f\u5df2\u50b3\u9001\uff01\")\n            }\n        }\n    ) {\n        Text(\"\u50b3\u9001\u8a0a\u606f\")\n    }\n}<\/code><\/pre>\n<ul>\n<li>viewModelScope \u95dc\u5fc3\u7684\u662f<strong>\u8cc7\u6599\u7684\u5b58\u6d3b<\/strong>\uff0c\u5b83\u6bd4 UI \u66f4\u6301\u4e45\uff0c\u80fd\u8de8\u8d8a\u77ed\u66ab\u7684 UI \u91cd\u5efa\uff08\u5982\u87a2\u5e55\u65cb\u8f49\uff09\u3002<\/li>\n<li>rememberCoroutineScope \u95dc\u5fc3\u7684\u662f<strong>\u756b\u9762\u7684\u5448\u73fe<\/strong>\uff0c\u5b83\u7684\u751f\u547d\u975e\u5e38\u77ed\u66ab\uff0c\u8207\u4f60\u5728\u87a2\u5e55\u4e0a\u770b\u5230\u7684\u5143\u4ef6\u540c\u751f\u5171\u6b7b\u3002<\/li>\n<\/ul>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<p>\u985e\u4f3c viewModelScope \u7684\u6709<\/p>\n<h3 class=\"wp-block-heading\">LifecycleScope<\/h3>\n<p>\u9019\u662f viewModelScope \u6700\u76f4\u63a5\u7684\u5144\u5f1f\u3002\u5982\u679c\u8aaa viewModelScope \u662f\u70ba ViewModel \u91cf\u8eab\u6253\u9020\u7684\uff0c\u90a3 LifecycleScope \u5c31\u662f\u70ba <strong>Activity \u548c Fragment<\/strong> \u9019\u985e\u5177\u6709\u660e\u78ba\u751f\u547d\u9031\u671f\u7684 UI \u5143\u4ef6\u6240\u8a2d\u8a08\u7684\u3002<\/p>\n<pre class=\"wp-block-code\"><code>class MyFragment : Fragment() {\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        \/\/ \u4f7f\u7528 lifecycleScope \u555f\u52d5\u5354\u7a0b\n        viewLifecycleOwner.lifecycleScope.launch {\n            \/\/ \u5728\u9019\u88e1\u57f7\u884c\u9577\u6642\u9593\u7684\u80cc\u666f\u4efb\u52d9\uff0c\u4f8b\u5982\u7db2\u8def\u8acb\u6c42\u6216\u8cc7\u6599\u5eab\u8b80\u53d6\n            val data = fetchData()\n            updateUi(data)\n        }\n    }\n}<\/code><\/pre>\n<h3 class=\"wp-block-heading\">GlobalScope (\u5168\u57df\u4f5c\u7528\u57df)<\/h3>\n<p>GlobalScope \u662f\u4e00\u500b<strong>\u91cd\u91cf\u7d1a<\/strong>\u7684\u5de5\u5177\uff0c\u9700\u8981\u8b39\u614e\u4f7f\u7528\u3002\u5b83\u6b63\u5982\u5176\u540d\uff0c\u662f\u4e00\u500b<strong>\u5168\u57df<\/strong>\u7684\u4f5c\u7528\u57df<\/p>\n<pre class=\"wp-block-code\"><code>\/\/ \u8b66\u544a\uff1a\u9664\u975e\u4f60\u975e\u5e38\u78ba\u5b9a\uff0c\u5426\u5247\u4e0d\u8981\u9019\u6a23\u505a\uff01\n\/\/ \u9019\u500b\u4efb\u52d9\u6703\u4e00\u76f4\u57f7\u884c\uff0c\u76f4\u5230 App \u88ab\u6bba\u6389\u3002\nGlobalScope.launch {\n    while (true) {\n        delay(30_000) \/\/ \u6bcf 30 \u79d2\u57f7\u884c\u4e00\u6b21\n        Log.d(\"GlobalScope\", \"Doing some background work...\")\n    }\n}\n\n<\/code><\/pre>\n<h3 class=\"wp-block-heading\">\u81ea\u8a02 CoroutineScope. \u5176\u5be6\u5c31\u662fSupervisorJob<\/h3>\n<p>\u9019\u5728\u8655\u7406\u975e\u6a19\u6e96\u751f\u547d\u9031\u671f\u7684\u7269\u4ef6\u6642\u7279\u5225\u6709\u7528\u3002<\/p>\n<pre class=\"wp-block-code\"><code>class BluetoothConnectionManager {\n\n    \/\/ 1. \u5efa\u7acb\u4e00\u500b\u5305\u542b Job \u548c Dispatcher \u7684\u81ea\u8a02 Scope\n    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)\n\n    fun startListening() {\n        scope.launch {\n            \/\/ \u958b\u59cb\u76e3\u807d\u85cd\u7259\u8a0a\u865f...\n        }\n    }\n\n    \/\/ 2. \u5fc5\u9808\u624b\u52d5\u5728\u9069\u7576\u7684\u6642\u6a5f\u53d6\u6d88\uff01\n    fun disconnect() {\n        \/\/ \u9019\u6703\u53d6\u6d88 scope \u5167\u6240\u6709\u7531\u5b83\u555f\u52d5\u7684\u5354\u7a0b\n        scope.cancel()\n    }\n}\n<\/code><\/pre>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<p>&#8212;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>viewModelScope\uff1a\u8207 ViewModel \u7684\u751f\u547d\u9031\u671f\u7d81\u5b9a\uff0c\u53ea\u80fd\u7528\u5728\u8cc7\u6599\/\u908f\u8f2f\u5c64\u3002 rememberCoroutineScope\uff1a\u8207 Composable \u756b\u9762\u5143\u4ef6\u7684\u751f\u547d\u9031\u671f\u7d81\u5b9a\uff0c\u53ea\u80fd\u7528\u5728 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":869,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[81],"tags":[],"_links":{"self":[{"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/posts\/634"}],"collection":[{"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/boochlin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=634"}],"version-history":[{"count":12,"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/posts\/634\/revisions"}],"predecessor-version":[{"id":870,"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/posts\/634\/revisions\/870"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/boochlin.com\/index.php?rest_route=\/wp\/v2\/media\/869"}],"wp:attachment":[{"href":"https:\/\/boochlin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=634"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/boochlin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=634"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/boochlin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=634"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}