[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FDMQP7W33F86SCKQCKDSWT":3,"skill-same:01M6FDMQP7W33F86SCKQCKDSWT":48},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":28,"repositoryHtmlUrl":29,"repositoryHomepageUrl":30,"ownerHtmlUrl":31,"ownerBlogUrl":6,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M6FDMQP7W33F86SCKQCKDSWT","android-coroutines",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fandroid-coroutines\u002FSKILL.md",[],[18,19,20],"android","coroutines","android coroutines","2026-11-10 10:12:30",false,"# Android Coroutines Expert Skill\n\nThis skill provides authoritative rules and patterns for writing production-quality Kotlin Coroutines code on Android. It enforces structured concurrency, lifecycle safety, and modern best practices (2025 standards).\n\n## Responsibilities\n\n*   **Asynchronous Logic**: Implementing suspend functions, Dispatcher management, and parallel execution.\n*   **Reactive Streams**: Implementing `Flow`, `StateFlow`, `SharedFlow`, and `callbackFlow`.\n*   **Lifecycle Integration**: Managing scopes (`viewModelScope`, `lifecycleScope`) and safe collection (`repeatOnLifecycle`).\n*   **Error Handling**: Implementing `CoroutineExceptionHandler`, `SupervisorJob`, and proper `try-catch` hierarchies.\n*   **Cancellability**: Ensuring long-running operations are cooperative using `ensureActive()`.\n*   **Testing**: Setting up `TestDispatcher` and `runTest`.\n\n## Applicability\n\nActivate this skill when the user asks to:\n*   \"Fetch data from an API\u002FDatabase.\"\n*   \"Perform background processing.\"\n*   \"Fix a memory leak\" related to threads\u002Ftasks.\n*   \"Convert a listener\u002Fcallback to Coroutines.\"\n*   \"Implement a ViewModel.\"\n*   \"Handle UI state updates.\"\n\n## Critical Rules & Constraints\n\n### 1. Dispatcher Injection (Testability)\n*   **NEVER** hardcode Dispatchers (e.g., `Dispatchers.IO`, `Dispatchers.Default`) inside classes.\n*   **ALWAYS** inject a `CoroutineDispatcher` via the constructor.\n*   **DEFAULT** to `Dispatchers.IO` in the constructor argument for convenience, but allow it to be overridden.\n\n```kotlin\n\u002F\u002F CORRECT\nclass UserRepository(\n    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO\n) { ... }\n\n\u002F\u002F INCORRECT\nclass UserRepository {\n    fun getData() = withContext(Dispatchers.IO) { ... }\n}\n```\n\n### 2. Main-Safety\n*   All suspend functions defined in the Data or Domain layer must be **main-safe**.\n*   **One-shot calls** should be exposed as `suspend` functions.\n*   **Data changes** should be exposed as `Flow`.\n*   The caller (ViewModel) should be able to call them from `Dispatchers.Main` without blocking the UI.\n*   Use `withContext(dispatcher)` inside the repository implementation to move execution to the background.\n\n### 3. Lifecycle-Aware Collection\n*   **NEVER** collect a flow directly in `lifecycleScope.launch` or `launchWhenStarted` (deprecated\u002Funsafe).\n*   **ALWAYS** use `repeatOnLifecycle(Lifecycle.State.STARTED)` for collecting flows in Activities or Fragments.\n\n```kotlin\n\u002F\u002F CORRECT\nviewLifecycleOwner.lifecycleScope.launch {\n    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {\n        viewModel.uiState.collect { ... }\n    }\n}\n```\n\n### 4. ViewModel Scope Usage\n*   Use `viewModelScope` for initiating coroutines in ViewModels.\n*   Do not expose suspend functions from the ViewModel to the View. The ViewModel should expose `StateFlow` or `SharedFlow` that the View observes.\n\n### 5. Mutable State Encapsulation\n*   **NEVER** expose `MutableStateFlow` or `MutableSharedFlow` publicly.\n*   Expose them as read-only `StateFlow` or `Flow` using `.asStateFlow()` or upcasting.\n\n### 6. GlobalScope Prohibition\n*   **NEVER** use `GlobalScope`. It breaks structured concurrency and leads to leaks.\n*   If a task must survive the current scope, use an injected `applicationScope` (a custom scope tied to the Application lifecycle).\n\n### 7. Exception Handling\n*   **NEVER** catch `CancellationException` in a generic `catch (e: Exception)` block without rethrowing it.\n*   Use `runCatching` only if you explicitly rethrow `CancellationException`.\n*   Use `CoroutineExceptionHandler` only for top-level coroutines (inside `launch`). It has no effect inside `async` or child coroutines.\n\n### 8. Cancellability\n*   Coroutines feature **cooperative cancellation**. They don't stop immediately unless they check for cancellation.\n*   **ALWAYS** call `ensureActive()` or `yield()` in tight loops (e.g., processing a large list, reading files) to check for cancellation.\n*   Standard functions like `delay()` and `withContext()` are already cancellable.\n\n### 9. Callback Conversion\n*   Use `callbackFlow` to convert callback-based APIs to Flow.\n*   **ALWAYS** use `awaitClose` at the end of the `callbackFlow` block to unregister listeners.\n\n## Code Patterns\n\n### Repository Pattern with Flow\n\n```kotlin\nclass NewsRepository(\n    private val remoteDataSource: NewsRemoteDataSource,\n    private val externalScope: CoroutineScope, \u002F\u002F For app-wide events\n    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO\n) {\n    val newsUpdates: Flow\u003CList\u003CNews>> = flow {\n        val news = remoteDataSource.fetchLatestNews()\n        emit(news)\n    }.flowOn(ioDispatcher) \u002F\u002F Upstream executes on IO\n}\n```\n\n### Parallel Execution\n\n```kotlin\nsuspend fun loadDashboardData() = coroutineScope {\n    val userDeferred = async { userRepo.getUser() }\n    val feedDeferred = async { feedRepo.getFeed() }\n    \n    \u002F\u002F Wait for both\n    DashboardData(\n        user = userDeferred.await(),\n        feed = feedDeferred.await()\n    )\n}\n```\n\n### Testing with runTest\n\n```kotlin\n@Test\nfun testViewModel() = runTest {\n    val testDispatcher = StandardTestDispatcher(testScheduler)\n    val viewModel = MyViewModel(testDispatcher)\n    \n    viewModel.loadData()\n    advanceUntilIdle() \u002F\u002F Process coroutines\n    \n    assertEquals(expectedState, viewModel.uiState.value)\n}\n```\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fandroid-coroutines",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill android-coroutines",{"tool":40,"command":41},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill android-coroutines",{"tool":43,"command":44},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill android-coroutines","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fandroid-coroutines\u002FSKILL.md",true,[],[49,63,73,82,88,95],{"skillNo":50,"name":51,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":52,"categories":53,"keywords":59,"publishedAt":21},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[54],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[60,61,62],"model","model hierarchy","model hierarchy skill",{"skillNo":64,"name":65,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":69,"publishedAt":21},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[68],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[70,71,72],"MCP","zoom","zoom mcp",{"skillNo":74,"name":75,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":21},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[79,80,81,71],"sdk","zoom apps sdk","apps",{"skillNo":83,"name":84,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":21},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[84],{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":21},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[93],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[90],{"skillNo":96,"name":97,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":100,"publishedAt":21},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[97]]