CINXE.COM
Testing Kotlin coroutines on Android | Android Developers
<!doctype html> <html lang="en" dir="ltr"> <head> <meta name="google-signin-client-id" content="721724668570-nbkv1cfusk7kk4eni4pjvepaus73b13t.apps.googleusercontent.com"> <meta name="google-signin-scope" content="profile email https://www.googleapis.com/auth/developerprofiles https://www.googleapis.com/auth/developerprofiles.award"> <meta property="og:site_name" content="Android Developers"> <meta property="og:type" content="website"><meta name="theme-color" content="#34a853"><meta charset="utf-8"> <meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="manifest" href="/_pwa/android/manifest.json" crossorigin="use-credentials"> <link rel="preconnect" href="//www.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.googleapis.com" crossorigin> <link rel="preconnect" href="//apis.google.com" crossorigin> <link rel="preconnect" href="//www.google-analytics.com" crossorigin><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Google+Sans:400,500,600,700|Google+Sans+Text:400,400italic,500,500italic,600,600italic,700,700italic|Roboto+Mono:400,500,700&display=swap"> <link rel="stylesheet" href="//fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block"><link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/css/app.css"> <link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/css/dark-theme.css" disabled> <link rel="shortcut icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/favicon.svg"> <link rel="apple-touch-icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/touchicon-180.png"><link rel="canonical" href="https://developer.android.com/kotlin/coroutines/test"><link rel="search" type="application/opensearchdescription+xml" title="Android Developers" href="https://developer.android.com/s/opensearch.xml"> <link rel="alternate" hreflang="en" href="https://developer.android.com/kotlin/coroutines/test" /><link rel="alternate" hreflang="x-default" href="https://developer.android.com/kotlin/coroutines/test" /><link rel="alternate" hreflang="ar" href="https://developer.android.com/kotlin/coroutines/test?hl=ar" /><link rel="alternate" hreflang="bn" href="https://developer.android.com/kotlin/coroutines/test?hl=bn" /><link rel="alternate" hreflang="zh-Hans" href="https://developer.android.com/kotlin/coroutines/test?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant" href="https://developer.android.com/kotlin/coroutines/test?hl=zh-tw" /><link rel="alternate" hreflang="fa" href="https://developer.android.com/kotlin/coroutines/test?hl=fa" /><link rel="alternate" hreflang="fr" href="https://developer.android.com/kotlin/coroutines/test?hl=fr" /><link rel="alternate" hreflang="de" href="https://developer.android.com/kotlin/coroutines/test?hl=de" /><link rel="alternate" hreflang="he" href="https://developer.android.com/kotlin/coroutines/test?hl=he" /><link rel="alternate" hreflang="hi" href="https://developer.android.com/kotlin/coroutines/test?hl=hi" /><link rel="alternate" hreflang="id" href="https://developer.android.com/kotlin/coroutines/test?hl=id" /><link rel="alternate" hreflang="it" href="https://developer.android.com/kotlin/coroutines/test?hl=it" /><link rel="alternate" hreflang="ja" href="https://developer.android.com/kotlin/coroutines/test?hl=ja" /><link rel="alternate" hreflang="ko" href="https://developer.android.com/kotlin/coroutines/test?hl=ko" /><link rel="alternate" hreflang="pl" href="https://developer.android.com/kotlin/coroutines/test?hl=pl" /><link rel="alternate" hreflang="pt-BR" href="https://developer.android.com/kotlin/coroutines/test?hl=pt-br" /><link rel="alternate" hreflang="ru" href="https://developer.android.com/kotlin/coroutines/test?hl=ru" /><link rel="alternate" hreflang="es-419" href="https://developer.android.com/kotlin/coroutines/test?hl=es-419" /><link rel="alternate" hreflang="th" href="https://developer.android.com/kotlin/coroutines/test?hl=th" /><link rel="alternate" hreflang="tr" href="https://developer.android.com/kotlin/coroutines/test?hl=tr" /><link rel="alternate" hreflang="vi" href="https://developer.android.com/kotlin/coroutines/test?hl=vi" /><title>Testing Kotlin coroutines on Android | Android Developers</title> <meta property="og:title" content="Testing Kotlin coroutines on Android | Android Developers"><meta property="og:url" content="https://developer.android.com/kotlin/coroutines/test"><meta property="og:image" content="https://developer.android.com/static/images/social/android-developers.png"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="675"><meta property="og:locale" content="en"><meta name="twitter:card" content="summary_large_image"><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "Testing Kotlin coroutines on Android" } </script><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Get started", "item": "https://developer.android.com/get-started/overview" },{ "@type": "ListItem", "position": 2, "name": "Kotlin", "item": "https://developer.android.com/kotlin" },{ "@type": "ListItem", "position": 3, "name": "Testing Kotlin coroutines on Android", "item": "https://developer.android.com/kotlin/coroutines/test" }] } </script> <link rel="stylesheet" href="/extras.css"></head> <body class="" template="page" theme="android-theme" type="article" appearance layout="docs" display-toc pending> <devsite-progress type="indeterminate" id="app-progress"></devsite-progress> <section class="devsite-wrapper"> <devsite-cookie-notification-bar></devsite-cookie-notification-bar><devsite-header role="banner"> <div class="devsite-header--inner nocontent"> <div class="devsite-top-logo-row-wrapper-wrapper"> <div class="devsite-top-logo-row-wrapper"> <div class="devsite-top-logo-row"> <button type="button" id="devsite-hamburger-menu" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Navigation menu button" visually-hidden aria-label="Open menu"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="androidDevelopers" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="Android Developers"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup.svg" class="devsite-site-logo" alt="Android Developers"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> <div class="devsite-top-logo-row-middle"> <div class="devsite-header-upper-tabs"> <devsite-tabs class="upper-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Upper tabs"> <tab class="devsite-dropdown devsite-dropdown-full devsite-active "> <a href="https://developer.android.com/kotlin" track-metadata-eventdetail="https://developer.android.com/kotlin" class="devsite-tabs-content gc-analytics-event android-dropdown-tab" track-type="nav" track-metadata-position="nav - essentials" track-metadata-module="primary nav" aria-label="Essentials, selected" data-category="Site-Wide Custom Events" data-label="Tab: Essentials" track-name="essentials" > Essentials </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="false" aria-label="Dropdown menu for Essentials" track-type="nav" track-metadata-eventdetail="https://developer.android.com/kotlin" track-metadata-position="nav - essentials" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Essentials" track-name="essentials" class="devsite-tabs-dropdown-toggle devsite-icon devsite-icon-arrow-drop-down"></a> <div class="devsite-tabs-dropdown" aria-label="submenu" hidden> <div class="devsite-tabs-dropdown-content"> <div class="devsite-tabs-dropdown-column android-dropdown android-dropdown-primary android-dropdown-studio"> <ul class="devsite-tabs-dropdown-section "> <li class="devsite-nav-title" role="heading" tooltip>Gemini in Android Studio</li> <li class="devsite-nav-description">Your AI development companion for Android development. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/gemini-in-android" track-type="nav" track-metadata-eventdetail="https://developer.android.com/gemini-in-android" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="gemini in android studio" tooltip class="button button-primary" > <div class="devsite-nav-item-title"> Learn more </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/studio" track-type="nav" track-metadata-eventdetail="https://developer.android.com/studio" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="gemini in android studio" tooltip class="button button-white" > <div class="devsite-nav-item-title"> Get Android Studio </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-launch"> <li class="devsite-nav-title" role="heading" tooltip>Get started</li> <li class="devsite-nav-description">Start by creating your first app. Go deeper with our training courses or explore app development on your own. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/get-started/overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/get-started/overview" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get started" tooltip > <div class="devsite-nav-item-title"> Hello world </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/courses" track-type="nav" track-metadata-eventdetail="https://developer.android.com/courses" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get started" tooltip > <div class="devsite-nav-item-title"> Training courses </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/get-started/codelabs" track-type="nav" track-metadata-eventdetail="https://developer.android.com/get-started/codelabs" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get started" tooltip > <div class="devsite-nav-item-title"> Tutorials </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/kotlin" track-type="nav" track-metadata-eventdetail="https://developer.android.com/kotlin" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get started" tooltip > <div class="devsite-nav-item-title"> Kotlin for Android </div> </a> </li> <li class="devsite-nav-item"> <a href="https://play.google.com/console/about/guides/monetize/" track-type="nav" track-metadata-eventdetail="https://play.google.com/console/about/guides/monetize/" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get started" tooltip > <div class="devsite-nav-item-title"> Monetization with Play ↗️ </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-multiple-screens"> <li class="devsite-nav-title" role="heading" tooltip>Extend by device</li> <li class="devsite-nav-description">Build apps that give your users seamless experiences from phones to tablets, watches, and more. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/adaptive-apps" track-type="nav" track-metadata-eventdetail="https://developer.android.com/adaptive-apps" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> Adaptive apps </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/wear" track-type="nav" track-metadata-eventdetail="https://developer.android.com/wear" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> Wear OS </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/cars" track-type="nav" track-metadata-eventdetail="https://developer.android.com/cars" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> Android for Cars </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/tv" track-type="nav" track-metadata-eventdetail="https://developer.android.com/tv" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> Android TV </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/chrome-os" track-type="nav" track-metadata-eventdetail="https://developer.android.com/chrome-os" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> ChromeOS </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/multi-device-development" track-type="nav" track-metadata-eventdetail="https://developer.android.com/multi-device-development" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="extend by device" tooltip > <div class="devsite-nav-item-title"> Cross-device SDK </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-platform"> <li class="devsite-nav-title" role="heading" tooltip>Build by category</li> <li class="devsite-nav-description">Learn to build for your use case by following Google's prescriptive and opinionated guidance. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/games" track-type="nav" track-metadata-eventdetail="https://developer.android.com/games" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Games </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/media" track-type="nav" track-metadata-eventdetail="https://developer.android.com/media" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Camera & Media </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/social-and-messaging" track-type="nav" track-metadata-eventdetail="https://developer.android.com/social-and-messaging" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Social & messaging </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/health-and-fitness" track-type="nav" track-metadata-eventdetail="https://developer.android.com/health-and-fitness" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Health & Fitness </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/productivity" track-type="nav" track-metadata-eventdetail="https://developer.android.com/productivity" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Productivity </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/work/overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/work/overview" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="build by category" tooltip > <div class="devsite-nav-item-title"> Enterprise apps </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-stars"> <li class="devsite-nav-title" role="heading" tooltip>Get the latest</li> <li class="devsite-nav-description">Stay in touch with the latest releases throughout the year, join our preview programs, and give us your feedback. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/latest-updates" track-type="nav" track-metadata-eventdetail="https://developer.android.com/latest-updates" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Latest updates </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/latest-updates/experimental" track-type="nav" track-metadata-eventdetail="https://developer.android.com/latest-updates/experimental" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Experimental updates </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/studio/preview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/studio/preview" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Android Studio preview </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/jetpack/androidx/versions" track-type="nav" track-metadata-eventdetail="https://developer.android.com/jetpack/androidx/versions" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Jetpack & Compose libraries </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/wearables/versions/4" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/wearables/versions/4" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Wear OS preview </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design-for-safety/privacy-sandbox" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design-for-safety/privacy-sandbox" track-metadata-position="nav - essentials" track-metadata-module="tertiary nav" track-metadata-module_headline="get the latest" tooltip > <div class="devsite-nav-item-title"> Privacy Sandbox </div> </a> </li> </ul> </div> </div> </div> </tab> <tab class="devsite-dropdown devsite-dropdown-full "> <a href="https://developer.android.com/design" track-metadata-eventdetail="https://developer.android.com/design" class="devsite-tabs-content gc-analytics-event android-dropdown-tab" track-type="nav" track-metadata-position="nav - design & plan" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Design & Plan" track-name="design & plan" > Design & Plan </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="false" aria-label="Dropdown menu for Design & Plan" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design" track-metadata-position="nav - design & plan" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Design & Plan" track-name="design & plan" class="devsite-tabs-dropdown-toggle devsite-icon devsite-icon-arrow-drop-down"></a> <div class="devsite-tabs-dropdown" aria-label="submenu" hidden> <div class="devsite-tabs-dropdown-content"> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-layout"> <li class="devsite-nav-title" role="heading" tooltip>UI Design</li> <li class="devsite-nav-description">Design a beautiful user interface using Android best practices.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Design for Android </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui/mobile" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui/mobile" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Mobile </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui/large-screens" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui/large-screens" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Large screens (e.g., tablets) </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui/widget" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui/widget" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Widgets </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui/wear" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui/wear" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Wear OS </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/design/ui/tv" track-type="nav" track-metadata-eventdetail="https://developer.android.com/design/ui/tv" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="ui design" tooltip > <div class="devsite-nav-item-title"> Android TV </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-design"> <li class="devsite-nav-title" role="heading" tooltip>Architecture</li> <li class="devsite-nav-description">Design robust, testable, and maintainable app logic and services.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/topic/architecture/intro" track-type="nav" track-metadata-eventdetail="https://developer.android.com/topic/architecture/intro" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="architecture" tooltip > <div class="devsite-nav-item-title"> Introduction </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/topic/libraries/view-binding" track-type="nav" track-metadata-eventdetail="https://developer.android.com/topic/libraries/view-binding" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="architecture" tooltip > <div class="devsite-nav-item-title"> Libraries </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/navigation/navigation-principles" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/navigation/navigation-principles" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="architecture" tooltip > <div class="devsite-nav-item-title"> Navigation </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/topic/modularization" track-type="nav" track-metadata-eventdetail="https://developer.android.com/topic/modularization" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="architecture" tooltip > <div class="devsite-nav-item-title"> Modularization </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/testing/fundamentals" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/testing/fundamentals" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="architecture" tooltip > <div class="devsite-nav-item-title"> Testing </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-vitals"> <li class="devsite-nav-title" role="heading" tooltip>Quality</li> <li class="devsite-nav-description">Plan for app quality and align with Play store guidelines.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quality" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quality" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="quality" tooltip > <div class="devsite-nav-item-title"> Overview </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quality/core-value" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quality/core-value" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="quality" tooltip > <div class="devsite-nav-item-title"> Core value </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quality/user-experience" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quality/user-experience" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="quality" tooltip > <div class="devsite-nav-item-title"> User experience </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quality/technical" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quality/technical" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="quality" tooltip > <div class="devsite-nav-item-title"> Technical quality </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quality/privacy-and-security" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quality/privacy-and-security" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="quality" tooltip > <div class="devsite-nav-item-title"> Security </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-security-2"> <li class="devsite-nav-title" role="heading" tooltip>Security</li> <li class="devsite-nav-description">Safeguard users against threats and ensure a secure Android experience.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/security" track-type="nav" track-metadata-eventdetail="https://developer.android.com/security" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="security" tooltip > <div class="devsite-nav-item-title"> Overview </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/privacy-and-security/about" track-type="nav" track-metadata-eventdetail="https://developer.android.com/privacy-and-security/about" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="security" tooltip > <div class="devsite-nav-item-title"> Privacy </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/identity" track-type="nav" track-metadata-eventdetail="https://developer.android.com/identity" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="security" tooltip > <div class="devsite-nav-item-title"> Identity </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/security/fraud-prevention" track-type="nav" track-metadata-eventdetail="https://developer.android.com/security/fraud-prevention" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="security" tooltip > <div class="devsite-nav-item-title"> Fraud prevention </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-earth"> <li class="devsite-nav-title" role="heading" tooltip>Build for Billions</li> <li class="devsite-nav-description">Create the best experience for entry-level devices</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/build-for-billions" track-type="nav" track-metadata-eventdetail="https://developer.android.com/build-for-billions" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="build for billions" tooltip > <div class="devsite-nav-item-title"> Overview </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/docs/quality-guidelines/build-for-billions" track-type="nav" track-metadata-eventdetail="https://developer.android.com/docs/quality-guidelines/build-for-billions" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="build for billions" tooltip > <div class="devsite-nav-item-title"> About new markets </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/topics/androidgo" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/topics/androidgo" track-metadata-position="nav - design & plan" track-metadata-module="tertiary nav" track-metadata-module_headline="build for billions" tooltip > <div class="devsite-nav-item-title"> Android (Go edition) </div> </a> </li> </ul> </div> </div> </div> </tab> <tab class="devsite-dropdown devsite-dropdown-full "> <a href="https://developer.android.com/develop" track-metadata-eventdetail="https://developer.android.com/develop" class="devsite-tabs-content gc-analytics-event android-dropdown-tab" track-type="nav" track-metadata-position="nav - develop" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Develop" track-name="develop" > Develop </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="false" aria-label="Dropdown menu for Develop" track-type="nav" track-metadata-eventdetail="https://developer.android.com/develop" track-metadata-position="nav - develop" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Develop" track-name="develop" class="devsite-tabs-dropdown-toggle devsite-icon devsite-icon-arrow-drop-down"></a> <div class="devsite-tabs-dropdown" aria-label="submenu" hidden> <div class="devsite-tabs-dropdown-content"> <div class="devsite-tabs-dropdown-column android-dropdown android-dropdown-primary android-dropdown-ai"> <ul class="devsite-tabs-dropdown-section "> <li class="devsite-nav-title" role="heading" tooltip>Build AI experiences</li> <li class="devsite-nav-description">Build AI-powered Android apps with Gemini APIs and more. </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/ai" track-type="nav" track-metadata-eventdetail="https://developer.android.com/ai" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="build ai experiences" tooltip class="button button-primary" > <div class="devsite-nav-item-title"> Get started </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-flag"> <li class="devsite-nav-title" role="heading" tooltip>Core areas</li> <li class="devsite-nav-description">Get the samples and docs for the features you need.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/samples" track-type="nav" track-metadata-eventdetail="https://developer.android.com/samples" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> Samples </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/quick-guides" track-type="nav" track-metadata-eventdetail="https://developer.android.com/quick-guides" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> Try Quick Guidesᵇᵉᵗᵃ </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/develop/ui" track-type="nav" track-metadata-eventdetail="https://developer.android.com/develop/ui" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> User interfaces </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/topics/permissions/overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/topics/permissions/overview" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> Permissions </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/develop/background-work" track-type="nav" track-metadata-eventdetail="https://developer.android.com/develop/background-work" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> Background work </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/topics/data" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/topics/data" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> Data and files </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/develop#core-areas" track-type="nav" track-metadata-eventdetail="https://developer.android.com/develop#core-areas" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="core areas" tooltip > <div class="devsite-nav-item-title"> All core areas ⤵️ </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-tools-2"> <li class="devsite-nav-title" role="heading" tooltip>Tools and workflow</li> <li class="devsite-nav-description">Use the IDE to write and build your app, or create your own pipeline.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/studio/write" track-type="nav" track-metadata-eventdetail="https://developer.android.com/studio/write" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Write and debug code </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/build/gradle-build-overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/build/gradle-build-overview" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Build projects </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/testing" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/testing" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Test your app </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/topic/performance/overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/topic/performance/overview" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Performance </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/tools" track-type="nav" track-metadata-eventdetail="https://developer.android.com/tools" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Command-line tools </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/reference/tools/gradle-api" track-type="nav" track-metadata-eventdetail="https://developer.android.com/reference/tools/gradle-api" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="tools and workflow" tooltip > <div class="devsite-nav-item-title"> Gradle plugin API </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-multiple-screens"> <li class="devsite-nav-title" role="heading" tooltip>Device tech</li> <li class="devsite-nav-description">Write code for form factors. Connect devices and share data.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/topics/large-screens/get-started-with-large-screens" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/topics/large-screens/get-started-with-large-screens" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Large screens (e.g., tablets) </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/wearables" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/wearables" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Wear OS </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/health-and-fitness/guides" track-type="nav" track-metadata-eventdetail="https://developer.android.com/health-and-fitness/guides" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Android Health </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/guide/topics/connectivity/cross-device-sdk/overview" track-type="nav" track-metadata-eventdetail="https://developer.android.com/guide/topics/connectivity/cross-device-sdk/overview" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Cross-device SDK </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/cars" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/cars" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Android for Cars </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/training/tv" track-type="nav" track-metadata-eventdetail="https://developer.android.com/training/tv" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> Android TV </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/chrome-os/intro" track-type="nav" track-metadata-eventdetail="https://developer.android.com/chrome-os/intro" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="device tech" tooltip > <div class="devsite-nav-item-title"> ChromeOS </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column android-dropdown"> <ul class="devsite-tabs-dropdown-section android-dropdown-section-icon android-dropdown-section-icon-core-library"> <li class="devsite-nav-title" role="heading" tooltip>Libraries</li> <li class="devsite-nav-description">Browse API reference documentation with all the details.</li> <li class="devsite-nav-item"> <a href="https://developer.android.com/reference/packages" track-type="nav" track-metadata-eventdetail="https://developer.android.com/reference/packages" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="libraries" tooltip > <div class="devsite-nav-item-title"> Android platform </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/jetpack/androidx/explorer" track-type="nav" track-metadata-eventdetail="https://developer.android.com/jetpack/androidx/explorer" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="libraries" tooltip > <div class="devsite-nav-item-title"> Jetpack libraries </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.android.com/jetpack/androidx/releases/compose" track-type="nav" track-metadata-eventdetail="https://developer.android.com/jetpack/androidx/releases/compose" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="libraries" tooltip > <div class="devsite-nav-item-title"> Compose libraries </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developers.google.com/android/reference/packages" track-type="nav" track-metadata-eventdetail="https://developers.google.com/android/reference/packages" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="libraries" tooltip > <div class="devsite-nav-item-title"> Google Play services ↗️ </div> </a> </li> <li class="devsite-nav-item"> <a href="https://play.google.com/sdks" track-type="nav" track-metadata-eventdetail="https://play.google.com/sdks" track-metadata-position="nav - develop" track-metadata-module="tertiary nav" track-metadata-module_headline="libraries" tooltip > <div class="devsite-nav-item-title"> Google Play SDK index ↗️ </div> </a> </li> </ul> </div> </div> </div> </tab> <tab > <a href="https://developer.android.com/distribute" track-metadata-eventdetail="https://developer.android.com/distribute" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - google play" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Google Play" track-name="google play" > Google Play </a> </tab> <tab > <a href="https://developer.android.com/community" track-metadata-eventdetail="https://developer.android.com/community" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - community" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Community" track-name="community" > Community </a> </tab> </nav> </devsite-tabs> </div> <devsite-search enable-signin enable-search enable-suggestions enable-query-completion project-name="Kotlin" tenant-name="Android Developers" > <form class="devsite-search-form" action="https://developer.android.com/s/results" method="GET"> <div class="devsite-search-container"> <button type="button" search-open class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Open search"></button> <div class="devsite-searchbox"> <input aria-activedescendant="" aria-autocomplete="list" aria-label="Search" aria-expanded="false" aria-haspopup="listbox" autocomplete="off" class="devsite-search-field devsite-search-query" name="q" placeholder="Search" role="combobox" type="text" value="" > <div class="devsite-search-image material-icons" aria-hidden="true"> </div> <div class="devsite-search-shortcut-icon-container" aria-hidden="true"> <kbd class="devsite-search-shortcut-icon">/</kbd> </div> </div> </div> </form> <button type="button" search-close class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Close search"></button> </devsite-search> </div> <devsite-appearance-selector></devsite-appearance-selector> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> <a class="devsite-header-link devsite-top-button button gc-analytics-event" href="https://developer.android.com/studio" data-category="Site-Wide Custom Events" data-label="Site header link" > Android Studio </a> <devsite-user enable-profiles id="devsite-user"> <span class="button devsite-top-button" aria-hidden="true" visually-hidden>Sign in</span> </devsite-user> </div> </div> </div> <div class="devsite-collapsible-section "> <div class="devsite-header-background"> <div class="devsite-product-id-row" > <div class="devsite-product-description-row"> <ul class="devsite-breadcrumb-list" aria-label="Lower header breadcrumb"> <li class="devsite-breadcrumb-item "> <a href="https://developer.android.com/get-started/overview" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Lower Header" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="Get started" > Get started </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://developer.android.com/kotlin" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Lower Header" data-value="2" track-type="globalNav" track-name="breadcrumb" track-metadata-position="2" track-metadata-eventdetail="Kotlin" > Kotlin </a> </li> </ul> </div> </div> <div class="devsite-doc-set-nav-row"> <devsite-tabs class="lower-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Lower tabs"> <tab > <a href="https://developer.android.com/kotlin" track-metadata-eventdetail="https://developer.android.com/kotlin" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - overview" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Overview" track-name="overview" > Overview </a> </tab> <tab > <a href="https://developer.android.com/kotlin/build-better-apps" track-metadata-eventdetail="https://developer.android.com/kotlin/build-better-apps" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - build better apps" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Build Better Apps" track-name="build better apps" > Build Better Apps </a> </tab> <tab > <a href="https://developer.android.com/kotlin/samples" track-metadata-eventdetail="https://developer.android.com/kotlin/samples" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - samples" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Samples" track-name="samples" > Samples </a> </tab> <tab class="devsite-active"> <a href="https://developer.android.com/kotlin/first" track-metadata-eventdetail="https://developer.android.com/kotlin/first" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - guides" track-metadata-module="primary nav" aria-label="Guides, selected" data-category="Site-Wide Custom Events" data-label="Tab: Guides" track-name="guides" > Guides </a> </tab> <tab > <a href="https://developer.android.com/kotlin/stories" track-metadata-eventdetail="https://developer.android.com/kotlin/stories" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - developer stories" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Developer Stories" track-name="developer stories" > Developer Stories </a> </tab> </nav> </devsite-tabs> </div> </div> </div> </div> </devsite-header> <devsite-book-nav scrollbars > <div class="devsite-book-nav-filter" > <span class="filter-list-icon material-icons" aria-hidden="true"></span> <input type="text" placeholder="Filter" aria-label="Type to filter" role="searchbox"> <span class="filter-clear-button hidden" data-title="Clear filter" aria-label="Clear filter" role="button" tabindex="0"></span> </div> <nav class="devsite-book-nav devsite-nav nocontent" aria-label="Side menu"> <div class="devsite-mobile-header"> <button type="button" id="devsite-close-nav" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Close navigation" aria-label="Close navigation"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="androidDevelopers" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="Android Developers"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup.svg" class="devsite-site-logo" alt="Android Developers"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> </div> <div class="devsite-book-nav-wrapper"> <div class="devsite-mobile-nav-top"> <ul class="devsite-nav-list"> <li class="devsite-nav-item"> <a href="/kotlin" class="devsite-nav-title gc-analytics-event devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Essentials" track-name="essentials" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Essentials" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Essentials </span> </a> <ul class="devsite-nav-responsive-tabs devsite-nav-has-menu "> <li class="devsite-nav-item"> <span class="devsite-nav-title" tooltip data-category="Site-Wide Custom Events" data-label="Tab: Essentials" track-name="essentials" > <span class="devsite-nav-text" tooltip menu="Essentials"> More </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="Essentials"> </span> </span> </li> </ul> <ul class="devsite-nav-responsive-tabs"> <li class="devsite-nav-item"> <a href="/kotlin" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Overview" track-name="overview" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Overview" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Overview </span> </a> </li> <li class="devsite-nav-item"> <a href="/kotlin/build-better-apps" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Build Better Apps" track-name="build better apps" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Build Better Apps" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Build Better Apps </span> </a> </li> <li class="devsite-nav-item"> <a href="/kotlin/samples" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Samples" track-name="samples" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Samples" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Samples </span> </a> </li> <li class="devsite-nav-item"> <a href="/kotlin/first" class="devsite-nav-title gc-analytics-event devsite-nav-has-children devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Guides" track-name="guides" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Guides" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip menu="_book"> Guides </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="_book"> </span> </a> </li> <li class="devsite-nav-item"> <a href="/kotlin/stories" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Developer Stories" track-name="developer stories" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Developer Stories" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Developer Stories </span> </a> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/design" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Design & Plan" track-name="design & plan" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Design & Plan" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Design & Plan </span> </a> <ul class="devsite-nav-responsive-tabs devsite-nav-has-menu "> <li class="devsite-nav-item"> <span class="devsite-nav-title" tooltip data-category="Site-Wide Custom Events" data-label="Tab: Design & Plan" track-name="design & plan" > <span class="devsite-nav-text" tooltip menu="Design & Plan"> More </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="Design & Plan"> </span> </span> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/develop" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Develop" track-name="develop" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Develop" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Develop </span> </a> <ul class="devsite-nav-responsive-tabs devsite-nav-has-menu "> <li class="devsite-nav-item"> <span class="devsite-nav-title" tooltip data-category="Site-Wide Custom Events" data-label="Tab: Develop" track-name="develop" > <span class="devsite-nav-text" tooltip menu="Develop"> More </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="Develop"> </span> </span> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/distribute" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Google Play" track-name="google play" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Google Play" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Google Play </span> </a> </li> <li class="devsite-nav-item"> <a href="/community" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Community" track-name="community" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Community" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Community </span> </a> </li> <li class="devsite-nav-item"> <a href="/studio" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android Studio" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android Studio </span> </a> </li> </ul> </div> <div class="devsite-mobile-nav-bottom"> <ul class="devsite-nav-list" menu="_book"> <li class="devsite-nav-item"><a href="/kotlin/first" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/first" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/first" ><span class="devsite-nav-text" tooltip>Kotlin-first Android development</span></a></li> <li class="devsite-nav-item devsite-nav-expandable devsite-nav-accordion"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Learning Android and Kotlin from scratch</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/androidbasics" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/androidbasics" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/androidbasics" ><span class="devsite-nav-text" tooltip>Android Basics with Compose</span></a></li><li class="devsite-nav-item"><a href="/kotlin/learn" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/learn" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/learn" ><span class="devsite-nav-text" tooltip>Learn the Kotlin language</span></a></li><li class="devsite-nav-item"><a href="/kotlin/common-patterns" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/common-patterns" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/common-patterns" ><span class="devsite-nav-text" tooltip>Use common Kotlin patterns with Android</span></a></li><li class="devsite-nav-item"><a href="/kotlin/style-guide" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/style-guide" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/style-guide" ><span class="devsite-nav-text" tooltip>Kotlin style guide</span></a></li><li class="devsite-nav-item"><a href="/kotlin/getting-started-resources" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/getting-started-resources" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/getting-started-resources" ><span class="devsite-nav-text" tooltip>Additional resources</span></a></li></ul></div></li> <li class="devsite-nav-item devsite-nav-expandable devsite-nav-accordion"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Kotlin for Android Java developers</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/add-kotlin" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/add-kotlin" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/add-kotlin" ><span class="devsite-nav-text" tooltip>Add Kotlin to an existing app</span></a></li><li class="devsite-nav-item"><a href="/kotlin/interop" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/interop" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/interop" ><span class="devsite-nav-text" tooltip>Kotlin-Java interop guide</span></a></li><li class="devsite-nav-item"><a href="/kotlin/learn-kotlin-java-pathway" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/learn-kotlin-java-pathway" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/learn-kotlin-java-pathway" ><span class="devsite-nav-text" tooltip>Learn Kotlin for Java developers</span></a></li><li class="devsite-nav-item"><a href="/kotlin/faq" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/faq" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/faq" ><span class="devsite-nav-text" tooltip>Kotlin on Android FAQ</span></a></li><li class="devsite-nav-item devsite-nav-expandable"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Android KTX</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/ktx" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/ktx" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/ktx" ><span class="devsite-nav-text" tooltip>Overview</span></a></li><li class="devsite-nav-item"><a href="/kotlin/ktx/extensions-list" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/ktx/extensions-list" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/ktx/extensions-list" ><span class="devsite-nav-text" tooltip>List of KTX extensions</span></a></li></ul></div></li><li class="devsite-nav-item"><a href="/kotlin/kotlin-java-resources" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/kotlin-java-resources" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/kotlin-java-resources" ><span class="devsite-nav-text" tooltip>Additional resources</span></a></li></ul></div></li> <li class="devsite-nav-item devsite-nav-expandable devsite-nav-accordion"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Advanced Kotlin guides</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item devsite-nav-expandable"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Kotlin coroutines</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/coroutines" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/coroutines" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/coroutines" ><span class="devsite-nav-text" tooltip>Introduction to coroutines</span></a></li><li class="devsite-nav-item"><a href="/kotlin/coroutines/coroutines-adv" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/coroutines/coroutines-adv" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/coroutines/coroutines-adv" ><span class="devsite-nav-text" tooltip>Advanced coroutines concepts</span></a></li><li class="devsite-nav-item"><a href="/kotlin/coroutines/test" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/coroutines/test" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/coroutines/test" ><span class="devsite-nav-text" tooltip>Testing coroutines</span></a></li><li class="devsite-nav-item"><a href="/kotlin/coroutines/coroutines-best-practices" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/coroutines/coroutines-best-practices" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/coroutines/coroutines-best-practices" ><span class="devsite-nav-text" tooltip>Coroutines best practices</span></a></li><li class="devsite-nav-item"><a href="/kotlin/coroutines/additional-resources" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/coroutines/additional-resources" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/coroutines/additional-resources" ><span class="devsite-nav-text" tooltip>Additional resources</span></a></li></ul></div></li><li class="devsite-nav-item devsite-nav-expandable"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Kotlin flows</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/flow" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/flow" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/flow" ><span class="devsite-nav-text" tooltip>Overview</span></a></li><li class="devsite-nav-item"><a href="/kotlin/flow/test" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/flow/test" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/flow/test" ><span class="devsite-nav-text" tooltip>Testing flows</span></a></li><li class="devsite-nav-item"><a href="/kotlin/flow/stateflow-and-sharedflow" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/flow/stateflow-and-sharedflow" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/flow/stateflow-and-sharedflow" ><span class="devsite-nav-text" tooltip>StateFlow and SharedFlow</span></a></li></ul></div></li><li class="devsite-nav-item"><a href="/kotlin/parcelize" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/parcelize" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/parcelize" ><span class="devsite-nav-text" tooltip>Parcelize</span></a></li><li class="devsite-nav-item devsite-nav-expandable"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Kotlin Multiplatform</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/multiplatform" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/multiplatform" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/multiplatform" ><span class="devsite-nav-text" tooltip>Overview</span></a></li><li class="devsite-nav-item"><a href="/kotlin/multiplatform/setup" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/multiplatform/setup" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/multiplatform/setup" ><span class="devsite-nav-text" tooltip>Create new Kotlin Multiplatform Project</span></a></li><li class="devsite-nav-item"><a href="/kotlin/multiplatform/room" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/multiplatform/room" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/multiplatform/room" ><span class="devsite-nav-text" tooltip>Room</span></a></li><li class="devsite-nav-item"><a href="/kotlin/multiplatform/sqlite" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/multiplatform/sqlite" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/multiplatform/sqlite" ><span class="devsite-nav-text" tooltip>SQLite</span></a></li><li class="devsite-nav-item"><a href="/kotlin/multiplatform/datastore" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/multiplatform/datastore" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/multiplatform/datastore" ><span class="devsite-nav-text" tooltip>DataStore</span></a></li></ul></div></li><li class="devsite-nav-item"><a href="/kotlin/get-certified" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/get-certified" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/get-certified" ><span class="devsite-nav-text" tooltip>Get certified</span></a></li><li class="devsite-nav-item"><a href="/kotlin/already-using-resources" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/already-using-resources" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/already-using-resources" ><span class="devsite-nav-text" tooltip>Additional resources</span></a></li></ul></div></li> <li class="devsite-nav-item devsite-nav-expandable devsite-nav-accordion"><div class="devsite-expandable-nav"> <a class="devsite-nav-toggle" aria-hidden="true"></a><div class="devsite-nav-title devsite-nav-title-no-path" tabindex="0" role="button"> <span class="devsite-nav-text" tooltip>Bringing Kotlin to your team</span> </div><ul class="devsite-nav-section"><li class="devsite-nav-item"><a href="/kotlin/adopt-for-large-teams" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/adopt-for-large-teams" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/adopt-for-large-teams" ><span class="devsite-nav-text" tooltip>Adopt Kotlin for large teams</span></a></li><li class="devsite-nav-item"><a href="/kotlin/stories" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /kotlin/stories" track-type="bookNav" track-name="click" track-metadata-eventdetail="/kotlin/stories" ><span class="devsite-nav-text" tooltip>Developer Stories</span></a></li></ul></div></li> </ul> <ul class="devsite-nav-list" menu="Essentials" aria-label="Side menu" hidden> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Gemini in Android Studio </span> </span> </li> <li class="devsite-nav-item"> <a href="/gemini-in-android" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Learn more" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Learn more </span> </a> </li> <li class="devsite-nav-item"> <a href="/studio" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Get Android Studio" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Get Android Studio </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Get started </span> </span> </li> <li class="devsite-nav-item"> <a href="/get-started/overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Hello world" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Hello world </span> </a> </li> <li class="devsite-nav-item"> <a href="/courses" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Training courses" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Training courses </span> </a> </li> <li class="devsite-nav-item"> <a href="/get-started/codelabs" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Tutorials" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Tutorials </span> </a> </li> <li class="devsite-nav-item"> <a href="/kotlin" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Kotlin for Android" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Kotlin for Android </span> </a> </li> <li class="devsite-nav-item"> <a href="https://play.google.com/console/about/guides/monetize/" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Monetization with Play ↗️" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Monetization with Play ↗️ </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Extend by device </span> </span> </li> <li class="devsite-nav-item"> <a href="/adaptive-apps" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Adaptive apps" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Adaptive apps </span> </a> </li> <li class="devsite-nav-item"> <a href="/wear" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Wear OS" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Wear OS </span> </a> </li> <li class="devsite-nav-item"> <a href="/cars" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android for Cars" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android for Cars </span> </a> </li> <li class="devsite-nav-item"> <a href="/tv" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android TV" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android TV </span> </a> </li> <li class="devsite-nav-item"> <a href="/chrome-os" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: ChromeOS" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > ChromeOS </span> </a> </li> <li class="devsite-nav-item"> <a href="/multi-device-development" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Cross-device SDK" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Cross-device SDK </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Build by category </span> </span> </li> <li class="devsite-nav-item"> <a href="/games" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Games" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Games </span> </a> </li> <li class="devsite-nav-item"> <a href="/media" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Camera & Media" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Camera & Media </span> </a> </li> <li class="devsite-nav-item"> <a href="/social-and-messaging" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Social & messaging" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Social & messaging </span> </a> </li> <li class="devsite-nav-item"> <a href="/health-and-fitness" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Health & Fitness" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Health & Fitness </span> </a> </li> <li class="devsite-nav-item"> <a href="/productivity" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Productivity" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Productivity </span> </a> </li> <li class="devsite-nav-item"> <a href="/work/overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Enterprise apps" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Enterprise apps </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Get the latest </span> </span> </li> <li class="devsite-nav-item"> <a href="/latest-updates" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Latest updates" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Latest updates </span> </a> </li> <li class="devsite-nav-item"> <a href="/latest-updates/experimental" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Experimental updates" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Experimental updates </span> </a> </li> <li class="devsite-nav-item"> <a href="/studio/preview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android Studio preview" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android Studio preview </span> </a> </li> <li class="devsite-nav-item"> <a href="/jetpack/androidx/versions" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Jetpack & Compose libraries" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Jetpack & Compose libraries </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/wearables/versions/4" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Wear OS preview" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Wear OS preview </span> </a> </li> <li class="devsite-nav-item"> <a href="/design-for-safety/privacy-sandbox" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Privacy Sandbox" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Privacy Sandbox </span> </a> </li> </ul> <ul class="devsite-nav-list" menu="Design & Plan" aria-label="Side menu" hidden> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > UI Design </span> </span> </li> <li class="devsite-nav-item"> <a href="/design/ui" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Design for Android" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Design for Android </span> </a> </li> <li class="devsite-nav-item"> <a href="/design/ui/mobile" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Mobile" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Mobile </span> </a> </li> <li class="devsite-nav-item"> <a href="/design/ui/large-screens" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Large screens (e.g., tablets)" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Large screens (e.g., tablets) </span> </a> </li> <li class="devsite-nav-item"> <a href="/design/ui/widget" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Widgets" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Widgets </span> </a> </li> <li class="devsite-nav-item"> <a href="/design/ui/wear" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Wear OS" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Wear OS </span> </a> </li> <li class="devsite-nav-item"> <a href="/design/ui/tv" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android TV" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android TV </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Architecture </span> </span> </li> <li class="devsite-nav-item"> <a href="/topic/architecture/intro" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Introduction" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Introduction </span> </a> </li> <li class="devsite-nav-item"> <a href="/topic/libraries/view-binding" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Libraries" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Libraries </span> </a> </li> <li class="devsite-nav-item"> <a href="/guide/navigation/navigation-principles" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Navigation" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Navigation </span> </a> </li> <li class="devsite-nav-item"> <a href="/topic/modularization" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Modularization" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Modularization </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/testing/fundamentals" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Testing" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Testing </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Quality </span> </span> </li> <li class="devsite-nav-item"> <a href="/quality" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Overview" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Overview </span> </a> </li> <li class="devsite-nav-item"> <a href="/quality/core-value" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Core value" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Core value </span> </a> </li> <li class="devsite-nav-item"> <a href="/quality/user-experience" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: User experience" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > User experience </span> </a> </li> <li class="devsite-nav-item"> <a href="/quality/technical" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Technical quality" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Technical quality </span> </a> </li> <li class="devsite-nav-item"> <a href="/quality/privacy-and-security" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Security" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Security </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Security </span> </span> </li> <li class="devsite-nav-item"> <a href="/security" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Overview" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Overview </span> </a> </li> <li class="devsite-nav-item"> <a href="/privacy-and-security/about" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Privacy" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Privacy </span> </a> </li> <li class="devsite-nav-item"> <a href="/identity" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Identity" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Identity </span> </a> </li> <li class="devsite-nav-item"> <a href="/security/fraud-prevention" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Fraud prevention" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Fraud prevention </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Build for Billions </span> </span> </li> <li class="devsite-nav-item"> <a href="/build-for-billions" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Overview" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Overview </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/quality-guidelines/build-for-billions" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: About new markets" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > About new markets </span> </a> </li> <li class="devsite-nav-item"> <a href="/guide/topics/androidgo" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android (Go edition)" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android (Go edition) </span> </a> </li> </ul> <ul class="devsite-nav-list" menu="Develop" aria-label="Side menu" hidden> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Build AI experiences </span> </span> </li> <li class="devsite-nav-item"> <a href="/ai" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Get started" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Get started </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Core areas </span> </span> </li> <li class="devsite-nav-item"> <a href="/samples" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Samples" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Samples </span> </a> </li> <li class="devsite-nav-item"> <a href="/quick-guides" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Try Quick Guidesᵇᵉᵗᵃ" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Try Quick Guidesᵇᵉᵗᵃ </span> </a> </li> <li class="devsite-nav-item"> <a href="/develop/ui" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: User interfaces" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > User interfaces </span> </a> </li> <li class="devsite-nav-item"> <a href="/guide/topics/permissions/overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Permissions" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Permissions </span> </a> </li> <li class="devsite-nav-item"> <a href="/develop/background-work" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Background work" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Background work </span> </a> </li> <li class="devsite-nav-item"> <a href="/guide/topics/data" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Data and files" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Data and files </span> </a> </li> <li class="devsite-nav-item"> <a href="/develop#core-areas" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: All core areas ⤵️" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > All core areas ⤵️ </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Tools and workflow </span> </span> </li> <li class="devsite-nav-item"> <a href="/studio/write" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Write and debug code" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Write and debug code </span> </a> </li> <li class="devsite-nav-item"> <a href="/build/gradle-build-overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Build projects" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Build projects </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/testing" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Test your app" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Test your app </span> </a> </li> <li class="devsite-nav-item"> <a href="/topic/performance/overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Performance" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Performance </span> </a> </li> <li class="devsite-nav-item"> <a href="/tools" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Command-line tools" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Command-line tools </span> </a> </li> <li class="devsite-nav-item"> <a href="/reference/tools/gradle-api" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Gradle plugin API" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Gradle plugin API </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Device tech </span> </span> </li> <li class="devsite-nav-item"> <a href="/guide/topics/large-screens/get-started-with-large-screens" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Large screens (e.g., tablets)" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Large screens (e.g., tablets) </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/wearables" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Wear OS" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Wear OS </span> </a> </li> <li class="devsite-nav-item"> <a href="/health-and-fitness/guides" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android Health" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android Health </span> </a> </li> <li class="devsite-nav-item"> <a href="/guide/topics/connectivity/cross-device-sdk/overview" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Cross-device SDK" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Cross-device SDK </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/cars" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android for Cars" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android for Cars </span> </a> </li> <li class="devsite-nav-item"> <a href="/training/tv" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android TV" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android TV </span> </a> </li> <li class="devsite-nav-item"> <a href="/chrome-os/intro" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: ChromeOS" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > ChromeOS </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Libraries </span> </span> </li> <li class="devsite-nav-item"> <a href="/reference/packages" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Android platform" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Android platform </span> </a> </li> <li class="devsite-nav-item"> <a href="/jetpack/androidx/explorer" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Jetpack libraries" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Jetpack libraries </span> </a> </li> <li class="devsite-nav-item"> <a href="/jetpack/androidx/releases/compose" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Compose libraries" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Compose libraries </span> </a> </li> <li class="devsite-nav-item"> <a href="https://developers.google.com/android/reference/packages" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Google Play services ↗️" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Google Play services ↗️ </span> </a> </li> <li class="devsite-nav-item"> <a href="https://play.google.com/sdks" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Google Play SDK index ↗️" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Google Play SDK index ↗️ </span> </a> </li> </ul> </div> </div> </nav> </devsite-book-nav> <section id="gc-wrapper"> <main role="main" class="devsite-main-content" has-book-nav has-sidebar > <div class="devsite-sidebar"> <div class="devsite-sidebar-content"> <devsite-toc class="devsite-nav" role="navigation" aria-label="On this page" depth="2" scrollbars ></devsite-toc> <devsite-recommendations-sidebar class="nocontent devsite-nav"> </devsite-recommendations-sidebar> </div> </div> <devsite-content> <article class="devsite-article"> <div class="devsite-article-meta nocontent" role="navigation"> <ul class="devsite-breadcrumb-list" aria-label="Breadcrumb"> <li class="devsite-breadcrumb-item "> <a href="https://developer.android.com/" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="Android Developers" > Android Developers </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://developer.android.com/get-started/overview" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="2" track-type="globalNav" track-name="breadcrumb" track-metadata-position="2" track-metadata-eventdetail="Get started" > Get started </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://developer.android.com/kotlin" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="3" track-type="globalNav" track-name="breadcrumb" track-metadata-position="3" track-metadata-eventdetail="Kotlin" > Kotlin </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://developer.android.com/kotlin/first" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="4" track-type="globalNav" track-name="breadcrumb" track-metadata-position="4" track-metadata-eventdetail="" > Guides </a> </li> </ul> <devsite-thumb-rating position="header"> </devsite-thumb-rating> </div> <h1 class="devsite-page-title" tabindex="-1"> Testing Kotlin coroutines on Android </h1> <devsite-feature-tooltip ack-key="AckCollectionsBookmarkTooltipDismiss" analytics-category="Site-Wide Custom Events" analytics-action-show="Callout Profile displayed" analytics-action-close="Callout Profile dismissed" analytics-label="Create Collection Callout" class="devsite-page-bookmark-tooltip nocontent" dismiss-button="true" id="devsite-collections-dropdown" dismiss-button-text="Dismiss" close-button-text="Got it"> <devsite-bookmark></devsite-bookmark> <span slot="popout-heading"> Stay organized with collections </span> <span slot="popout-contents"> Save and categorize content based on your preferences. </span> </devsite-feature-tooltip> <div class="devsite-page-title-meta"><devsite-view-release-notes></devsite-view-release-notes></div> <devsite-toc class="devsite-nav" depth="2" devsite-toc-embedded > </devsite-toc> <devsite-recommendations-dropdown class="nocontent"></devsite-recommendations-dropdown> <div class="devsite-article-body clearfix "> <p>Unit testing code that uses <a href="/kotlin/coroutines">coroutines</a> requires some extra attention, as their execution can be asynchronous and happen across multiple threads. This guide covers how suspending functions can be tested, the testing constructs you need to be familiar with, and how to make your code that uses coroutines testable.</p> <p>The APIs used in this guide are part of the <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/" class="external">kotlinx.coroutines.test</a> library. Make sure to <a href="https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test#using-in-your-project" class="external">add the artifact</a> as a test dependency to your project to have access to these APIs.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><code translate="no" dir="ltr"><span class="devsite-syntax-n">dependencies</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">testImplementation</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"org.jetbrains.kotlinx:kotlinx-coroutines-test:</span><span class="devsite-syntax-si">$</span><span class="devsite-syntax-n">coroutines_version</span><span class="devsite-syntax-s">"</span> <span class="devsite-syntax-p">}</span> </code></pre></devsite-code><aside class="note"><strong>Note:</strong><span> This guide uses the coroutine testing APIs introduced in kotlinx.coroutines.test 1.6. If you’re looking to migrate from an earlier version, use the <a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md" class="external">migration guide</a>.</span></aside> <h2 id="invoking-suspending-functions" data-text="Invoking suspending functions in tests" tabindex="-1">Invoking suspending functions in tests</h2> <p>To call suspending functions in tests, you need to be in a coroutine. As JUnit test functions themselves aren't suspending functions, you need to call a coroutine builder inside your tests to start a new coroutine.</p> <p><a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html" class="external"><code translate="no" dir="ltr">runTest</code></a> is a coroutine builder designed for testing. Use this to wrap any tests that include coroutines. Note that coroutines can be started not only directly in the test body, but also by the objects being used in the test.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">suspend</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">fetchData</span><span class="devsite-syntax-p">():</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kt">String</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">delay</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-m">1000L</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">return</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Hello world"</span> <span class="devsite-syntax-p">}</span> <span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">dataShouldBeHelloWorld</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">data</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">fetchData</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Hello world"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">data</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/SuspendingFunctionTests.kt#L26-L35" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/SuspendingFunctionTests.kt#coroutine_test_runtest" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/SuspendingFunctionTests.kt" data-region-tag="coroutine_test_runtest"><span class="devsite-syntax-n">SuspendingFunctionTests</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>In general, you should have one invocation of <code translate="no" dir="ltr">runTest</code> per test, and using an <a href="https://kotlinlang.org/docs/functions.html#single-expression-functions" class="external">expression body</a> is recommended.</p> <aside class="key-point"><strong>Key Point:</strong><span> Use <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html" class="external"><code translate="no" dir="ltr">runTest</code></a> for tests that use coroutines.</span></aside> <p>Wrapping your test’s code in <code translate="no" dir="ltr">runTest</code> will work for testing basic suspending functions, and it will automatically skip any delays in coroutines, making the test above complete much faster than one second.</p> <p>However, there are additional considerations to make, depending on what happens in your code under test:</p> <ul> <li>When your code creates new coroutines other than the top-level test coroutine that <code translate="no" dir="ltr">runTest</code> creates, you’ll need to control how those new coroutines are scheduled by <a href="#testdispatchers">choosing the appropriate <code translate="no" dir="ltr">TestDispatcher</code></a>.</li> <li>If your code moves the coroutine execution to other dispatchers (for example, by using <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html" class="external"><code translate="no" dir="ltr">withContext</code></a>), <code translate="no" dir="ltr">runTest</code> will still generally work, but delays will no longer be skipped, and tests will be less predictable as code runs on multiple threads. For these reasons, in tests you should <a href="#injecting-test-dispatchers">inject test dispatchers</a> to replace real dispatchers.</li> </ul> <h2 id="testdispatchers" data-text="TestDispatchers" tabindex="-1">TestDispatchers</h2> <p><a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/index.html" class="external"><code translate="no" dir="ltr">TestDispatchers</code></a> are <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html" class="external"><code translate="no" dir="ltr">CoroutineDispatcher</code></a> implementations for testing purposes. You’ll need to use <code translate="no" dir="ltr">TestDispatchers</code> if new coroutines are created during the test to make the execution of the new coroutines predictable.</p> <aside class="note"><strong>Note:</strong><span> New coroutines can be created directly in the test body, but also in any of the code you’re calling in your test (for example, inside the objects you’re testing).</span></aside> <p>There are two available implementations of <code translate="no" dir="ltr">TestDispatcher</code>: <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-standard-test-dispatcher.html" class="external"><code translate="no" dir="ltr">StandardTestDispatcher</code></a> and <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html" class="external"><code translate="no" dir="ltr">UnconfinedTestDispatcher</code></a>, which perform different scheduling of newly-started coroutines. These both use a <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/index.html" class="external"><code translate="no" dir="ltr">TestCoroutineScheduler</code></a> to control virtual time and manage running coroutines within a test.</p> <p><strong>There should only be one scheduler instance used in a test</strong>, shared between all <code translate="no" dir="ltr">TestDispatchers</code>. See <a href="#injecting-test-dispatchers">Injecting TestDispatchers</a> to learn about sharing schedulers.</p> <p>To start the top-level test coroutine, <code translate="no" dir="ltr">runTest</code> creates a <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope.html" class="external"><code translate="no" dir="ltr">TestScope</code></a>, which is an implementation of <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/" class="external"><code translate="no" dir="ltr">CoroutineScope</code></a> that will always use a <code translate="no" dir="ltr">TestDispatcher</code>. If not specified, a <code translate="no" dir="ltr">TestScope</code> will create a <code translate="no" dir="ltr">StandardTestDispatcher</code> by default, and use that to run the top-level test coroutine.</p> <p><code translate="no" dir="ltr">runTest</code> keeps track of the coroutines that are queued on the scheduler used by the dispatcher of its <code translate="no" dir="ltr">TestScope</code>, and will not return as long as there’s pending work on that scheduler.</p> <aside class="key-point"><strong>Key Point:</strong><span> <code translate="no" dir="ltr">runTest</code> runs the test coroutine in a <code translate="no" dir="ltr">TestScope</code>, using a <code translate="no" dir="ltr">TestDispatcher. TestDispatchers</code> use a <code translate="no" dir="ltr">TestCoroutineScheduler</code> to control virtual time and schedule new coroutines in a test. All <code translate="no" dir="ltr">TestDispatchers</code> in a test must use the same scheduler instance.</span></aside> <h3 id="standardtestdispatcher" data-text="StandardTestDispatcher" tabindex="-1">StandardTestDispatcher</h3> <p>When you start new coroutines on a <code translate="no" dir="ltr">StandardTestDispatcher</code>, they are queued up on the underlying scheduler, to be run whenever the test thread is free to use. To let these new coroutines run, you need to <em>yield</em> the test thread (free it up for other coroutines to use). This queueing behavior gives you precise control over how new coroutines run during the test, and it resembles the scheduling of coroutines in production code.</p> <aside class="note"><strong>Note:</strong><span> For simplicity, the examples here show new coroutines started directly within <code translate="no" dir="ltr">runTest</code>, as <code translate="no" dir="ltr">runTest</code> uses a <code translate="no" dir="ltr">StandardTestDispatcher</code> by default. However, the scheduling behavior shown here applies to any coroutine started on a <code translate="no" dir="ltr">StandardTestDispatcher</code>, including ones <a href="#injecting-test-dispatchers">injected</a> into classes under test.</span></aside> <p>If the test thread is never yielded during the execution of the top-level test coroutine, any new coroutines will only run after the test coroutine is done (but before <code translate="no" dir="ltr">runTest</code> returns):</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">standardTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserRepository</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">listOf</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">),</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">getAllUsers</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ❌ Fails</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt#L29-L37" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt#coroutine_test_standard_failing" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt" data-region-tag="coroutine_test_standard_failing"><span class="devsite-syntax-n">StandardTestDispatcherTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>There are several ways to yield the test coroutine to let queued-up coroutines run. All of these calls let other coroutines run on the test thread before returning:</p> <ul> <li><a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/advance-until-idle.html" class="external"><code translate="no" dir="ltr">advanceUntilIdle</code></a>: Runs all other coroutines on the scheduler until there is nothing left in the queue. This is a good default choice to let all pending coroutines run, and it will work in most test scenarios.</li> <li><a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/advance-time-by.html" class="external"><code translate="no" dir="ltr">advanceTimeBy</code></a>: Advances virtual time by the given amount and runs any coroutines scheduled to run before that point in virtual time.</li> <li><a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/run-current.html" class="external"><code translate="no" dir="ltr">runCurrent</code></a>: Runs coroutines that are scheduled at the current virtual time.</li> </ul> <p>To fix the previous test, <code translate="no" dir="ltr">advanceUntilIdle</code> can be used to let the two pending coroutines perform their work before continuing to the assertion:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">standardTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserRepository</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">advanceUntilIdle</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Yields to perform the registrations</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">listOf</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">),</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">getAllUsers</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ✅ Passes</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt#L43-L52" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt#coroutine_test_standard_fixed" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/StandardTestDispatcherTest.kt" data-region-tag="coroutine_test_standard_fixed"><span class="devsite-syntax-n">StandardTestDispatcherTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <aside class="note"><strong>Note:</strong><span> You could also <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html" class="external"><code translate="no" dir="ltr">join</code></a> the <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html" class="external"><code translate="no" dir="ltr">Job</code></a> instances returned by the <code translate="no" dir="ltr">launch</code> calls to make sure that the new coroutines are done before performing the assertion.</span></aside><aside class="key-point"><strong>Key Point:</strong><span> <code translate="no" dir="ltr">StandardTestDispatcher</code> offers precise control over the execution of coroutines, which is useful in complex test scenarios. <code translate="no" dir="ltr">runTest</code> uses a <code translate="no" dir="ltr">StandardTestDispatcher</code> by default.</span></aside> <h3 id="unconfinedtestdispatcher" data-text="UnconfinedTestDispatcher" tabindex="-1">UnconfinedTestDispatcher</h3> <p>When new coroutines are started on an <code translate="no" dir="ltr">UnconfinedTestDispatcher</code>, they are started eagerly on the current thread. This means that they’ll start running immediately, without waiting for their coroutine builder to return. In many cases, this dispatching behavior results in simpler test code, as you don’t need to manually yield the test thread to let new coroutines run.</p> <p>However, this behavior is different from what you’ll see in production with non-test dispatchers. If your test focuses on concurrency, prefer using <code translate="no" dir="ltr">StandardTestDispatcher</code> instead.</p> <aside class="key-point"><strong>Key Point:</strong><span> <code translate="no" dir="ltr">UnconfinedTestDispatcher</code> executes new coroutines eagerly, and it can be a good choice for simple tests with coroutines.</span></aside> <p>To use this dispatcher for the top-level test coroutine in <code translate="no" dir="ltr">runTest</code> instead of the default one, create an instance and pass it in as a parameter. This will make new coroutines created within <code translate="no" dir="ltr">runTest</code> execute eagerly, as they inherit the dispatcher from the <code translate="no" dir="ltr">TestScope</code>.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">unconfinedTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserRepository</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">listOf</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">),</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">getAllUsers</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ✅ Passes</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt#L29-L37" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt#coroutine_test_unconfined" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt" data-region-tag="coroutine_test_unconfined"><span class="devsite-syntax-n">UnconfinedTestDispatcherTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>In this example, the launch calls will start their new coroutines eagerly on the <code translate="no" dir="ltr">UnconfinedTestDispatcher</code>, which means that each call to launch will only return after the registration is completed.</p> <aside class="key-point"><strong>Key Point:</strong><span> You can pass in a <code translate="no" dir="ltr">TestDispatcher</code> to <code translate="no" dir="ltr">runTest</code> to control the dispatching behavior in the top-level test coroutine.</span></aside><aside class="note"><strong>Note:</strong><span> Using <code translate="no" dir="ltr">runTest</code> with an <code translate="no" dir="ltr">UnconfinedTestDispatcher</code> provides the same scheduling behavior as <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html" class="external"><code translate="no" dir="ltr">runBlockingTest</code></a>, which was part of the coroutine testing APIs before version 1.6.</span></aside> <p>Remember that <code translate="no" dir="ltr">UnconfinedTestDispatcher</code> starts new coroutines eagerly, but this doesn’t mean that it’ll run them to completion eagerly as well. If the new coroutine suspends, other coroutines will resume executing.</p> <p>For example, the new coroutine launched within this test will register Alice, but then it suspends when <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html" class="external"><code translate="no" dir="ltr">delay</code></a> is called. This lets the top-level coroutine proceed with the assertion, and the test fails as Bob is not registered yet:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">yieldingTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserRepository</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">delay</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-m">10L</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">listOf</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Alice"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Bob"</span><span class="devsite-syntax-p">),</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepo</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">getAllUsers</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ❌ Fails</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt#L42-L53" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt#coroutine_test_unconfined_yielding" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UnconfinedTestDispatcherTest.kt" data-region-tag="coroutine_test_unconfined_yielding"><span class="devsite-syntax-n">UnconfinedTestDispatcherTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <h2 id="injecting-test-dispatchers" data-text="Injecting test dispatchers" tabindex="-1">Injecting test dispatchers</h2> <p>Code under test might use dispatchers to switch threads (using <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html" class="external"><code translate="no" dir="ltr">withContext</code></a>) or to start new coroutines. When code is executed on multiple threads in parallel, tests can become flaky. It can be difficult to perform assertions at the correct time or to wait for tasks to complete if they’re running on background threads that you have no control over.</p> <p>In tests, replace these dispatchers with instances of <code translate="no" dir="ltr">TestDispatchers</code>. This has several benefits:</p> <ul> <li>The code will run on the single test thread, making tests more deterministic</li> <li>You can control how new coroutines are scheduled and executed</li> <li>TestDispatchers use a scheduler for virtual time, which skips delays automatically and lets you advance time manually</li> </ul> <p>Using <a href="/training/dependency-injection">dependency injection</a> to provide dispatchers to your classes makes it easy to replace the real dispatchers in tests. In these examples, we’ll inject a <code translate="no" dir="ltr">CoroutineDispatcher</code>, but you can also inject the broader <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/" class="external"><code translate="no" dir="ltr">CoroutineContext</code></a> type, which allows for even more flexibility during tests.</p> <p>For classes that start coroutines, you can also inject a <code translate="no" dir="ltr">CoroutineScope</code> instead of a dispatcher, as detailed in the <a href="#inject-scope">Injecting a scope</a> section.</p> <aside class="key-point"><strong>Key Point:</strong><span> In tests, replace real dispatchers with instances of <code translate="no" dir="ltr">TestDispatchers</code> to ensure that all code runs on the single test thread.</span></aside><aside class="note"><strong>Note:</strong><span> See the <a href="#setting-main-dispatcher">Setting the Main dispatcher</a> section to learn how to replace the Main dispatcher in tests.</span></aside> <p><code translate="no" dir="ltr">TestDispatchers</code> will, by default, create a new scheduler when they’re instantiated. Inside <code translate="no" dir="ltr">runTest</code>, you can access the <code translate="no" dir="ltr">testScheduler</code> property of the <code translate="no" dir="ltr">TestScope</code> and pass it in to any newly created <code translate="no" dir="ltr">TestDispatchers</code>. This will share their understanding of virtual time, and methods like <code translate="no" dir="ltr">advanceUntilIdle</code> will run coroutines on all test dispatchers to completion.</p> <aside class="caution"><strong>Caution:</strong><span> You can create and use any number of <code translate="no" dir="ltr">TestDispatchers</code> within a test, however, they must all share the same scheduler. Be careful not to create multiple schedulers.</span></aside> <p>In the following example, you can see a <code translate="no" dir="ltr">Repository</code> class that creates a new coroutine using the <code translate="no" dir="ltr">IO</code> dispatcher in its <code translate="no" dir="ltr">initialize</code> method and switches the caller to the <code translate="no" dir="ltr">IO</code> dispatcher in its <code translate="no" dir="ltr">fetchData</code> method:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-c1">// Example class demonstrating dispatcher use cases</span> <span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">Repository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">ioDispatcher</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">IO</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">scope</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineScope</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">ioDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">initialized</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">AtomicBoolean</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kc">false</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// A function that starts a new coroutine on the IO dispatcher</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">initialize</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">scope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">initialized</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">set</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kc">true</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// A suspending function that switches to the IO dispatcher</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">suspend</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">fetchData</span><span class="devsite-syntax-p">():</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kt">String</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">withContext</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">ioDispatcher</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">require</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">initialized</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">get</span><span class="devsite-syntax-p">())</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Repository should be initialized first"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">delay</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-m">500L</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Hello world"</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt#L29-L47" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt#coroutine_test_repo_dispatcher_injection" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt" data-region-tag="coroutine_test_repo_dispatcher_injection"><span class="devsite-syntax-n">Repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>In tests, you can inject a <code translate="no" dir="ltr">TestDispatcher</code> implementation to replace the <code translate="no" dir="ltr">IO</code> dispatcher.</p> <p>In the example below, we inject a <code translate="no" dir="ltr">StandardTestDispatcher</code> into the repository, and use <code translate="no" dir="ltr">advanceUntilIdle</code> to make sure that the new coroutine started in <code translate="no" dir="ltr">initialize</code> completes before proceeding.</p> <aside class="caution"><strong>Caution:</strong><span> Advancing until the new coroutine is done is only possible because the new coroutine uses a <code translate="no" dir="ltr">TestDispatcher</code>. Having to do this reveals that the <code translate="no" dir="ltr">initialize</code> method in the example above is not a well-designed API. It starts asynchronous work that callers need to wait for, but has no way to notify callers when that work is done.</span></aside> <p><code translate="no" dir="ltr">fetchData</code> will also benefit from running on a <code translate="no" dir="ltr">TestDispatcher</code>, as it will run on the test thread and skip the delay it contains during the test.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">RepositoryTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">repoInitWorksAndDataIsHelloWorld</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">dispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StandardTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testScheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Repository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">dispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">initialize</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">advanceUntilIdle</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Runs the new coroutine</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kc">true</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">initialized</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">get</span><span class="devsite-syntax-p">())</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">data</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">fetchData</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// No thread switch, delay is skipped</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Hello world"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">data</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt#L10-L23" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt#coroutine_test_repo_dispatcher_injection_test" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt" data-region-tag="coroutine_test_repo_dispatcher_injection_test"><span class="devsite-syntax-n">RepositoryTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>New coroutines started on a <code translate="no" dir="ltr">TestDispatcher</code> can be advanced manually as shown above with <code translate="no" dir="ltr">initialize</code>. Note, however, that this would not be possible or desirable in production code. Instead, this method should be redesigned to be either suspending (for sequential execution), or to return a <code translate="no" dir="ltr">Deferred</code> value (for concurrent execution).</p> <p>For example, you can use <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html" class="external"><code translate="no" dir="ltr">async</code></a> to start a new coroutine and create a <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html" class="external"><code translate="no" dir="ltr">Deferred</code></a>:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">BetterRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">ioDispatcher</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">IO</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">scope</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineScope</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">ioDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">initialize</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">scope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">async</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt#L51-L69" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt#coroutine_test_repo_dispatcher_injection_better" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/Repository.kt" data-region-tag="coroutine_test_repo_dispatcher_injection_better"><span class="devsite-syntax-n">Repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>This lets you safely <code translate="no" dir="ltr">await</code> the completion of this code in both tests and production code:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">repoInitWorks</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">dispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StandardTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testScheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">BetterRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">dispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">initialize</span><span class="devsite-syntax-p">().</span><span class="devsite-syntax-na">await</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Suspends until the new coroutine is done</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kc">true</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">initialized</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">get</span><span class="devsite-syntax-p">())</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ...</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt#L28-L40" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt#coroutine_test_repo_dispatcher_injection_test_better" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/RepositoryTest.kt" data-region-tag="coroutine_test_repo_dispatcher_injection_test_better"><span class="devsite-syntax-n">RepositoryTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <aside class="note"><strong>Note:</strong><span> Similarly, you can create a new coroutine with <code translate="no" dir="ltr">launch</code> and then call <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html" class="external"><code translate="no" dir="ltr">join</code></a> on the <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html" class="external"><code translate="no" dir="ltr">Job</code></a> that it returns to wait for its completion.</span></aside> <p><code translate="no" dir="ltr">runTest</code> will wait for pending coroutines to complete before returning if the coroutines are on a <code translate="no" dir="ltr">TestDispatcher</code> that it shares a scheduler with. It will also wait for coroutines that are children of the top-level test coroutine, even if they’re on other dispatchers (up to a timeout specified by the <code translate="no" dir="ltr">dispatchTimeoutMs</code> parameter, which is 60 seconds by default).</p> <h2 id="setting-main-dispatcher" data-text="Setting the Main dispatcher" tabindex="-1">Setting the Main dispatcher</h2> <p>In <a href="/training/testing/local-tests">local unit tests</a>, the <code translate="no" dir="ltr">Main</code> dispatcher that wraps the Android UI thread will be unavailable, as these tests are executed on a local JVM and not an Android device. If your code under test references the main thread, it’ll throw an exception during unit tests.</p> <aside class="note"><strong>Note:</strong><span> This only applies to local unit tests. You should not replace the <code translate="no" dir="ltr">Main</code> dispatcher in <a href="/training/testing/instrumented-tests">instrumented tests</a> where the real UI thread is available.</span></aside> <p>In some cases, you can inject the <code translate="no" dir="ltr">Main</code> dispatcher the same way as other dispatchers, as described in <a href="#injecting-test-dispatchers">the previous section</a>, allowing you to replace it with a <code translate="no" dir="ltr">TestDispatcher</code> in tests. However, some APIs such as <a href="/topic/libraries/architecture/coroutines#viewmodelscope"><code translate="no" dir="ltr">viewModelScope</code></a> use a hardcoded <code translate="no" dir="ltr">Main</code> dispatcher under the hood.</p> <p>Here’s an example of a <a href="/topic/libraries/architecture/viewmodel"><code translate="no" dir="ltr">ViewModel</code></a> implementation that uses <code translate="no" dir="ltr">viewModelScope</code> to launch a coroutine that loads data:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">HomeViewModel</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">ViewModel</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">_message</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MutableStateFlow</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">""</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">message</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StateFlow<String></span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">get</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">_message</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">loadMessage</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">viewModelScope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">_message</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">value</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s">"Greetings!"</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/main/kotlin/com/example/android/coroutines/testing/HomeViewModel.kt#L26-L35" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/HomeViewModel.kt#coroutine_test_homeviewmodel" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/HomeViewModel.kt" data-region-tag="coroutine_test_homeviewmodel"><span class="devsite-syntax-n">HomeViewModel</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>To replace the <code translate="no" dir="ltr">Main</code> dispatcher with a <code translate="no" dir="ltr">TestDispatcher</code> in all cases, use the <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html" class="external"><code translate="no" dir="ltr">Dispatchers.setMain</code></a> and <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/reset-main.html" class="external"><code translate="no" dir="ltr">Dispatchers.resetMain</code></a> functions.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">HomeViewModelTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">settingMainDispatcher</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testScheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">setMain</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">try</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">viewModel</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">HomeViewModel</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">viewModel</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">loadMessage</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Uses testDispatcher, runs its coroutine eagerly</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Greetings!"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">viewModel</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">message</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">value</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">finally</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">resetMain</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTest.kt#L28-L42" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTest.kt#coroutine_test_homeviewmodel_test" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTest.kt" data-region-tag="coroutine_test_homeviewmodel_test"><span class="devsite-syntax-n">HomeViewModelTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p><strong>If the <code translate="no" dir="ltr">Main</code> dispatcher has been replaced with a <code translate="no" dir="ltr">TestDispatcher</code>, any newly-created <code translate="no" dir="ltr">TestDispatchers</code> will automatically use the scheduler from the <code translate="no" dir="ltr">Main</code> dispatcher</strong>, including the <code translate="no" dir="ltr">StandardTestDispatcher</code> created by <code translate="no" dir="ltr">runTest</code> if no other dispatcher is passed to it.</p> <p>This makes it easier to ensure that there is only a single scheduler in use during the test. For this to work, make sure to create all other <code translate="no" dir="ltr">TestDispatcher</code> instances <em>after</em> calling <code translate="no" dir="ltr">Dispatchers.setMain</code>.</p> <aside class="key-point"><strong>Key Point:</strong><span> The <code translate="no" dir="ltr">Main</code> dispatcher should be replaced by a <code translate="no" dir="ltr">TestDispatcher</code> in local unit tests. Replacing the <code translate="no" dir="ltr">Main</code> dispatcher also simplifies sharing schedulers between <code translate="no" dir="ltr">TestDispatchers</code>.</span></aside> <p>A common pattern to avoid duplicating the code that replaces the <code translate="no" dir="ltr">Main</code> dispatcher in each test is to extract it into a JUnit <a href="https://junit.org/junit4/javadoc/4.12/org/junit/rules/TestRule.html" class="external">test rule</a>:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-c1">// Reusable JUnit4 TestRule to override the Main dispatcher</span> <span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">MainDispatcherRule</span><span class="devsite-syntax-p">(</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testDispatcher</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">TestDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">(),</span> <span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">TestWatcher</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">override</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">starting</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">description</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Description</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">setMain</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">override</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">finished</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">description</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Description</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Dispatchers</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">resetMain</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span> <span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">HomeViewModelTestUsingRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@get</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-n">Rule</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">mainDispatcherRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MainDispatcherRule</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">settingMainDispatcher</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Uses Main’s scheduler</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">viewModel</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">HomeViewModel</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">viewModel</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">loadMessage</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Greetings!"</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">viewModel</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">message</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">value</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTestUsingRule.kt#L32-L55" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTestUsingRule.kt#coroutine_test_homeviewmodeltest_with_rule" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/HomeViewModelTestUsingRule.kt" data-region-tag="coroutine_test_homeviewmodeltest_with_rule"><span class="devsite-syntax-n">HomeViewModelTestUsingRule</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>This rule implementation uses an <code translate="no" dir="ltr">UnconfinedTestDispatcher</code> by default, but a <code translate="no" dir="ltr">StandardTestDispatcher</code> can be passed in as a parameter if the <code translate="no" dir="ltr">Main</code> dispatcher shouldn’t execute eagerly in a given test class.</p> <p>When you need a <code translate="no" dir="ltr">TestDispatcher</code> instance in the test body, you can reuse the <code translate="no" dir="ltr">testDispatcher</code> from the rule, as long as it’s the desired type. If you want to be explicit about the type of <code translate="no" dir="ltr">TestDispatcher</code> used in the test, or if you need a <code translate="no" dir="ltr">TestDispatcher</code> that’s a different type than the one used for <code translate="no" dir="ltr">Main</code>, you can create a new <code translate="no" dir="ltr">TestDispatcher</code> within <code translate="no" dir="ltr">runTest</code>. As the <code translate="no" dir="ltr">Main</code> dispatcher is set to a <code translate="no" dir="ltr">TestDispatcher</code>, any newly created <code translate="no" dir="ltr">TestDispatchers</code> will share its scheduler automatically.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">DispatcherTypesTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@get</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-n">Rule</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">mainDispatcherRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MainDispatcherRule</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">injectingTestDispatchers</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Uses Main’s scheduler</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Use the UnconfinedTestDispatcher from the Main dispatcher</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">unconfinedRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Repository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">mainDispatcherRule</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Create a new StandardTestDispatcher (uses Main’s scheduler)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">standardRepo</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">Repository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">StandardTestDispatcher</span><span class="devsite-syntax-p">())</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatcherTypesTest.kt#L25-L37" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatcherTypesTest.kt#coroutine_test_dispatcher_types" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatcherTypesTest.kt" data-region-tag="coroutine_test_dispatcher_types"><span class="devsite-syntax-n">DispatcherTypesTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <h2 id="creating-dispatchers" data-text="Creating dispatchers outside a test" tabindex="-1">Creating dispatchers outside a test</h2> <p>In some cases, you might need a <code translate="no" dir="ltr">TestDispatcher</code> to be available outside the test method. For example, during the initialization of a property in the test class:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">ExampleRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">ioDispatcher</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineDispatcher</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-cm">/* ... */</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">RepositoryTestWithRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">ExampleRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-cm">/* What TestDispatcher? */</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@get</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-n">Rule</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">mainDispatcherRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MainDispatcherRule</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">someRepositoryTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Test the repository...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#L37-L52" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#coroutine_test_repo_with_rule_blank" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt" data-region-tag="coroutine_test_repo_with_rule_blank"><span class="devsite-syntax-n">DispatchersOutsideTests</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>If you’re replacing the <code translate="no" dir="ltr">Main</code> dispatcher as shown in the previous section, <code translate="no" dir="ltr">TestDispatchers</code> created <em>after</em> the <code translate="no" dir="ltr">Main</code> dispatcher has been replaced will automatically share its scheduler.</p> <p>This isn’t the case, however, for <code translate="no" dir="ltr">TestDispatchers</code> created as properties of the test class or <code translate="no" dir="ltr">TestDispatchers</code> created during the initialization of properties in the test class. These are initialized before the <code translate="no" dir="ltr">Main</code> dispatcher is replaced. Therefore, they would create new schedulers.</p> <aside class="caution"><strong>Caution:</strong><span> If you create a <code translate="no" dir="ltr">TestDispatcher</code> as a property of a test class, it won’t take its scheduler from the <code translate="no" dir="ltr">Main</code> dispatcher, as the <code translate="no" dir="ltr">Main</code> dispatcher is only replaced before each test method is executed.</span></aside> <p>To make sure that there’s only one scheduler in your test, create the <code translate="no" dir="ltr">MainDispatcherRule</code> property first. Then reuse its dispatcher (or its scheduler, if you need a <code translate="no" dir="ltr">TestDispatcher</code> of a different type) in the initializers of other class-level properties as needed.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">RepositoryTestWithRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@get</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-n">Rule</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">mainDispatcherRule</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MainDispatcherRule</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">ExampleRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">mainDispatcherRule</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">someRepositoryTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Takes scheduler from Main</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Any TestDispatcher created here also takes the scheduler from Main</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">newTestDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StandardTestDispatcher</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Test the repository...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#L58-L71" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#coroutine_test_repo_with_rule" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt" data-region-tag="coroutine_test_repo_with_rule"><span class="devsite-syntax-n">DispatchersOutsideTests</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>Note that both <code translate="no" dir="ltr">runTest</code> and <code translate="no" dir="ltr">TestDispatchers</code> created within the test will still automatically share the scheduler of the <code translate="no" dir="ltr">Main</code> dispatcher.</p> <p>If you’re not replacing the <code translate="no" dir="ltr">Main</code> dispatcher, create your first <code translate="no" dir="ltr">TestDispatcher</code> (which creates a new scheduler) as a property of the class. Then, manually pass that scheduler to each <code translate="no" dir="ltr">runTest</code> invocation and each new <code translate="no" dir="ltr">TestDispatcher</code> created, both as properties and within the test:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">RepositoryTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Creates the single test scheduler</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">ExampleRepository</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">someRepositoryTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">scheduler</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Take the scheduler from the TestScope</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">newTestDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-k">this</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">testScheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Or take the scheduler from the first dispatcher, they’re the same</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">anotherTestDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UnconfinedTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">scheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Test the repository...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#L75-L89" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt#coroutine_test_repo_without_rule" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/DispatchersOutsideTests.kt" data-region-tag="coroutine_test_repo_without_rule"><span class="devsite-syntax-n">DispatchersOutsideTests</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>In this sample, the scheduler from the first dispatcher is passed to <code translate="no" dir="ltr">runTest</code>. This will create a new <code translate="no" dir="ltr">StandardTestDispatcher</code> for the <code translate="no" dir="ltr">TestScope</code> using that scheduler. You could also pass in the dispatcher to <code translate="no" dir="ltr">runTest</code> directly to run the test coroutine on that dispatcher.</p> <h2 id="creating-your-own-testscope" data-text="Creating your own TestScope" tabindex="-1">Creating your own TestScope</h2> <p>Like with <code translate="no" dir="ltr">TestDispatchers</code>, you might need to access a <code translate="no" dir="ltr">TestScope</code> outside the test body. While <code translate="no" dir="ltr">runTest</code> creates a <code translate="no" dir="ltr">TestScope</code> under the hood automatically, you can also create your own <code translate="no" dir="ltr">TestScope</code> to use with <code translate="no" dir="ltr">runTest</code>.</p> <p>When doing this, make sure to call <code translate="no" dir="ltr">runTest</code> on the <code translate="no" dir="ltr">TestScope</code> you’ve created:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">SimpleExampleTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testScope</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">TestScope</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Creates a StandardTestDispatcher</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">someTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">testScope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt#L27-L34" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt#coroutine_test_your_own_scope" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt" data-region-tag="coroutine_test_your_own_scope"><span class="devsite-syntax-n">CreatingYourOwn</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <aside class="caution"><strong>Caution:</strong><span> If you create your own <code translate="no" dir="ltr">TestScope</code>, you must call <code translate="no" dir="ltr">runTest</code> on that scope within your test. There can only be one <code translate="no" dir="ltr">TestScope</code> instance in a test.</span></aside> <p>The code above creates a <code translate="no" dir="ltr">StandardTestDispatcher</code> for the <code translate="no" dir="ltr">TestScope</code> implicitly, as well as a new scheduler. These objects can all also be created explicitly. This can be useful if you need to integrate it with dependency injection setups.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">ExampleTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testScheduler</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">TestCoroutineScheduler</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testDispatcher</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StandardTestDispatcher</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testScheduler</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">testScope</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">TestScope</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">testDispatcher</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">someTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">testScope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// ...</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt#L38-L47" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt#coroutine_test_your_own_everything" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/CreatingYourOwn.kt" data-region-tag="coroutine_test_your_own_everything"><span class="devsite-syntax-n">CreatingYourOwn</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <aside class="key-point"><strong>Key Point:</strong><span> You can create all the coroutine testing constructs outside of tests if your project setup requires it.</span></aside> <h2 id="inject-scope" data-text="Injecting a scope" tabindex="-1">Injecting a scope</h2> <p>If you have a class that creates coroutines that you need to control during tests, you can inject a coroutine scope into that class, replacing it with a <code translate="no" dir="ltr">TestScope</code> in tests.</p> <p>In the following example, the <code translate="no" dir="ltr">UserState</code> class depends on a <code translate="no" dir="ltr">UserRepository</code> to register new users and to fetch the list of registered users. As these calls to <code translate="no" dir="ltr">UserRepository</code> are suspending function calls, <code translate="no" dir="ltr">UserState</code> uses the injected <code translate="no" dir="ltr">CoroutineScope</code> to start a new coroutine inside its <code translate="no" dir="ltr">registerUser</code> function.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">UserState</span><span class="devsite-syntax-p">(</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userRepository</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserRepository</span><span class="devsite-syntax-p">,</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">scope</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">CoroutineScope</span><span class="devsite-syntax-p">,</span> <span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">private</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">_users</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">MutableStateFlow</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">emptyList<String></span><span class="devsite-syntax-p">())</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">users</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">StateFlow<List<String></span>><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">_users</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">asStateFlow</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">registerUser</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">name</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kt">String</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">scope</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">launch</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">register</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">name</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">_users</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">update</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userRepository</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">getAllUsers</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/main/kotlin/com/example/android/coroutines/testing/UserState.kt#L29-L42" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/UserState.kt#android_coroutine_test_user_state" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/main/kotlin/com/example/android/coroutines/testing/UserState.kt" data-region-tag="android_coroutine_test_user_state"><span class="devsite-syntax-n">UserState</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <p>To test this class, you can pass in the <code translate="no" dir="ltr">TestScope</code> from <code translate="no" dir="ltr">runTest</code> when creating the <code translate="no" dir="ltr">UserState</code> object:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Kotlin"><span class="devsite-syntax-kd">class</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nc">UserStateTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nd">@Test</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">fun</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">addUserTest</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">runTest</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// this: TestScope</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">repository</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">FakeUserRepository</span><span class="devsite-syntax-p">()</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-kd">val</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nv">userState</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">UserState</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">repository</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">scope</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">this</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userState</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">registerUser</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Mona"</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">advanceUntilIdle</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-c1">// Let the coroutine complete and changes propagate</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">assertEquals</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-n">listOf</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s">"Mona"</span><span class="devsite-syntax-p">),</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-n">userState</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">users</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">value</span><span class="devsite-syntax-p">)</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span><div class="devsite-github-link nocode no-select"><a target="_top" href="https://github.com/android/snippets/blob/f595a0d317fd8aca6b33d4344bf87696cd45c481/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UserStateTest.kt#L14-L25" class="gc-analytics-event" data-category="github_link" data-label="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UserStateTest.kt#android_coroutine_test_user_state_test" data-code-snippet="true" data-github-path="android/snippets/kotlin/src/test/kotlin/com/example/android/coroutines/testing/UserStateTest.kt" data-region-tag="android_coroutine_test_user_state_test"><span class="devsite-syntax-n">UserStateTest</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-na">kt</span></a></div></pre></devsite-code> <aside class="tip"><strong>Tip:</strong><span> If your class creates coroutines that don't complete on their own and should be canceled at the end of the test, you can inject <a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/background-scope.html" class="external"><code translate="no" dir="ltr">TestScope.backgroundScope</code></a> instead of the <code translate="no" dir="ltr">TestScope</code> itself.</span></aside> <p>To inject a scope outside of the test function, for example into an object under test that's created as a property in the test class, see <a href="#creating-your-own-testscope">Creating your own TestScope</a>.</p> <h2 id="additional-resources" data-text="Additional resources" tabindex="-1">Additional resources</h2> <ul> <li><a href="/kotlin/flow/test">Testing Kotlin flows on Android</a></li> <li><a href="https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test" class="external">kotlinx.coroutines.test</a> on GitHub</li> </ul> <devsite-hats-survey class="nocontent" hats-id="VxqvKSur40kxBYCLVTd0SSGykbno" listnr-id="5207477"></devsite-hats-survey> </div> <devsite-recommendations display="in-page" hidden yield> </devsite-recommendations> <devsite-thumb-rating position="footer"> </devsite-thumb-rating> <devsite-recommendations id="recommendations-link" yield></devsite-recommendations> <div class="devsite-floating-action-buttons"> </div> </article> <devsite-content-footer class="nocontent"> <p>Content and code samples on this page are subject to the licenses described in the <a href="/license">Content License</a>. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.</p> <p>Last updated 2024-10-15 UTC.</p> </devsite-content-footer> <devsite-notification > </devsite-notification> <div class="devsite-content-data"> <template class="devsite-content-data-template"> [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2024-10-15 UTC."],[],[]] </template> </div> </devsite-content> </main> <devsite-footer-promos class="devsite-footer"> <nav class="devsite-footer-promos nocontent" aria-label="Promotions"> <ul class="devsite-footer-promos-list"> <li class="devsite-footer-promo"> <a href="//x.com/AndroidDev" class="devsite-footer-promo-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer X Promo" > <picture> <source class="devsite-dark-theme" srcset="https://developer.android.com/_static/android/images/logo-x_dt.svg" media="(prefers-color-scheme: dark)" loading="lazy" alt="X"> <img class="devsite-footer-promo-icon" src="/_static/android/images/logo-x.svg" loading="lazy" alt="X"> </picture> <span class="devsite-footer-promo-label"> X </span> </a> <div class="devsite-footer-promo-description">Follow @AndroidDev on X</div> </li> <li class="devsite-footer-promo"> <a href="//www.youtube.com/user/androiddevelopers" class="devsite-footer-promo-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer YouTube Promo" > <picture> <source class="devsite-dark-theme" srcset="https://developer.android.com/_static/android/images/logo-youtube_dt.svg" media="(prefers-color-scheme: dark)" loading="lazy" alt="YouTube"> <img class="devsite-footer-promo-icon" src="//www.gstatic.com/images/icons/material/product/2x/youtube_48dp.png" loading="lazy" alt="YouTube"> </picture> <span class="devsite-footer-promo-label"> YouTube </span> </a> <div class="devsite-footer-promo-description">Check out Android Developers on YouTube</div> </li> <li class="devsite-footer-promo"> <a href="//www.linkedin.com/showcase/androiddev" class="devsite-footer-promo-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer LinkedIn Promo" > <picture> <source class="devsite-dark-theme" srcset="https://developer.android.com/_static/android/images/logo-linkedin_dt.svg" media="(prefers-color-scheme: dark)" loading="lazy" alt="LinkedIn"> <img class="devsite-footer-promo-icon" src="/_static/android/images/logo-linkedin.svg" loading="lazy" alt="LinkedIn"> </picture> <span class="devsite-footer-promo-label"> LinkedIn </span> </a> <div class="devsite-footer-promo-description">Connect with the Android Developers community on LinkedIn</div> </li> </ul> </nav> </devsite-footer-promos> <devsite-footer-linkboxes class="devsite-footer"> <nav class="devsite-footer-linkboxes nocontent" aria-label="Footer links"> <ul class="devsite-footer-linkboxes-list"> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">More Android</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="//www.android.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Android </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//www.android.com/enterprise/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Android for Enterprise </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//www.android.com/security-center/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Security </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//source.android.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Source </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/news" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > News </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//android-developers.googleblog.com/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 6)" > Blog </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/podcasts" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 7)" > Podcasts </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Discover</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="/games" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Gaming </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/ml" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Machine Learning </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/health-and-fitness" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Health & Fitness </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/media" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Camera & Media </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/privacy" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Privacy </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/training/connectivity/5g" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 6)" > 5G </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Android Devices</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="/large-screens" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Large screens </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/wear" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Wear OS </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/chrome-os" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > ChromeOS devices </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/cars" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Android for cars </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/tv" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Android TV </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Releases</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/15" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Android 15 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/14" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Android 14 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/13" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Android 13 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/12" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Android 12 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/11" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Android 11 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/10" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 6)" > Android 10 </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/about/versions/pie" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 7)" > Pie </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Documentation and Downloads</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="/studio/intro" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Android Studio guide </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/guide" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Developers guides </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/reference" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > API reference </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/studio" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Download Studio </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/ndk" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Android NDK </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Support</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="//issuetracker.google.com/issues/new?component=190923&template=841312" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Report platform bug </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//issuetracker.google.com/issues/new?component=192697" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Report documentation bug </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//support.google.com/googleplay/android-developer" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Google Play support </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://g.co/userresearch/androiddeveloperfooter" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Join research studies </a> </li> </ul> </li> </ul> </nav> </devsite-footer-linkboxes> <devsite-footer-utility class="devsite-footer"> <div class="devsite-footer-utility nocontent"> <nav class="devsite-footer-sites" aria-label="Other Google Developers websites"> <a href="https://developers.google.com/" class="devsite-footer-sites-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Google Developers Link"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup-google-for-developers-dark-theme.svg" media="(prefers-color-scheme: none)" class="devsite-dark-theme" loading="lazy" alt="Google Developers"> <img class="devsite-footer-sites-logo" src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup-google-for-developers.svg" loading="lazy" alt="Google Developers"> </picture> </a> <ul class="devsite-footer-sites-list"> <li class="devsite-footer-sites-item"> <a href="//developer.android.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Android Link" > Android </a> </li> <li class="devsite-footer-sites-item"> <a href="//developer.chrome.com/home" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Chrome Link" > Chrome </a> </li> <li class="devsite-footer-sites-item"> <a href="//firebase.google.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Firebase Link" > Firebase </a> </li> <li class="devsite-footer-sites-item"> <a href="//cloud.google.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Google Cloud Platform Link" > Google Cloud Platform </a> </li> <li class="devsite-footer-sites-item"> <a href="//developers.google.com/products/" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer All products Link" > All products </a> </li> </ul> </nav> <nav class="devsite-footer-utility-links" aria-label="Utility links"> <ul class="devsite-footer-utility-list"> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/privacy" data-category="Site-Wide Custom Events" data-label="Footer Privacy link" > Privacy </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="/license" data-category="Site-Wide Custom Events" data-label="Footer License link" > License </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="/distribute/marketing-tools/brand-guidelines" data-category="Site-Wide Custom Events" data-label="Footer Brand guidelines link" > Brand guidelines </a> </li> <li class="devsite-footer-utility-item glue-cookie-notification-bar-control"> <a class="devsite-footer-utility-link gc-analytics-event" href="#" data-category="Site-Wide Custom Events" data-label="Footer Manage cookies link" aria-hidden="true" > Manage cookies </a> </li> <li class="devsite-footer-utility-item devsite-footer-utility-button"> <span class="devsite-footer-utility-description">Get news and tips by email</span> <a class="devsite-footer-utility-link gc-analytics-event" href="/updates" data-category="Site-Wide Custom Events" data-label="Footer Subscribe link" > Subscribe </a> </li> </ul> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> </nav> </div> </devsite-footer-utility> <devsite-panel></devsite-panel> </section></section> <devsite-sitemask></devsite-sitemask> <devsite-snackbar></devsite-snackbar> <devsite-tooltip ></devsite-tooltip> <devsite-heading-link></devsite-heading-link> <devsite-analytics> <script type="application/json" analytics>[]</script> <script type="application/json" tag-management>{"at": "True", "ga4": [{"id": "G-QFRN08RN6E", "purpose": 0}], "ga4p": [{"id": "G-QFRN08RN6E", "purpose": 0}], "gtm": [{"id": "GTM-KMSWPCJ", "purpose": 0}], "parameters": {"internalUser": "False", "language": {"machineTranslated": "False", "requested": "en", "served": "en"}, "pageType": "article", "projectName": "Kotlin", "signedIn": "False", "tenant": "android", "recommendations": {"sourcePage": "", "sourceType": 0, "sourceRank": 0, "sourceIdenticalDescriptions": 0, "sourceTitleWords": 0, "sourceDescriptionWords": 0, "experiment": ""}, "experiment": {"ids": ""}}}</script> </devsite-analytics> <devsite-badger></devsite-badger> <android-fully-clickable target=" .android-case-study .devsite-landing-row-item, .android-grouped-resources .devsite-landing-row-item, .android-grouped-resources-primary .devsite-landing-row-item, .android-grouped-resources-secondary .devsite-landing-row-item, .android-guide-cards .devsite-landing-row-item, .android-illustrated-resources-index .devsite-landing-row-item, .android-illustrated-resources-primary .devsite-landing-row-item, .android-illustrated-resources-secondary .devsite-landing-row-item, .android-illustrated-resources-secondary-small .devsite-landing-row-item, .android-illustrated-resources-tertiary .devsite-landing-row-item, .android-illustrated-resources-tertiary-small .devsite-landing-row-item, .android-promo .devsite-landing-row-item, .android-quick-link, .android-samples .devsite-card-wrapper, .fully-clickable" watch=".android-samples, devsite-content"></android-fully-clickable> <script nonce="AZ9UQqYAkoi8Zwrtaai88p/tyiOY5G"> (function(d,e,v,s,i,t,E){d['GoogleDevelopersObject']=i; t=e.createElement(v);t.async=1;t.src=s;E=e.getElementsByTagName(v)[0]; E.parentNode.insertBefore(t,E);})(window, document, 'script', 'https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/js/app_loader.js', '[3,"en",null,"/js/devsite_app_module.js","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android","https://android-dot-devsite-v2-prod.appspot.com",null,null,["/_pwa/android/manifest.json","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/images/video-placeholder.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/favicon.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/android/images/lockup.svg","https://fonts.googleapis.com/css?family=Google+Sans:400,500,600,700|Google+Sans+Text:400,400italic,500,500italic,600,600italic,700,700italic|Roboto+Mono:400,500,700&display=swap"],1,null,[1,6,8,12,14,17,21,25,50,52,63,70,75,76,80,87,91,92,93,97,98,100,101,102,103,104,105,107,108,109,110,112,113,117,118,120,122,124,125,126,127,129,130,131,132,133,134,135,136,138,140,141,147,148,149,151,152,156,157,158,159,161,163,164,168,169,170,179,180,182,183,186,191,193,196],"AIzaSyAP-jjEJBzmIyKR4F-3XITp8yM9T1gEEI8","AIzaSyB6xiKGDR5O3Ak2okS4rLkauxGUG7XP0hg","developer.android.com","AIzaSyAQk0fBONSGUqCNznf6Krs82Ap1-NV6J4o","AIzaSyCCxcqdrZ_7QMeLCRY20bh_SXdAYqy70KY",null,null,null,["Significatio__enable_by_tenant","MiscFeatureFlags__enable_explain_this_code","Cloud__enable_cloudx_experiment_ids","CloudShell__cloud_code_overflow_menu","Analytics__enable_clearcut_logging","Profiles__require_profile_eligibility_for_signin","Search__enable_ai_search_summaries","Profiles__enable_complete_playlist_endpoint","EngEduTelemetry__enable_engedu_telemetry","Search__enable_dynamic_content_confidential_banner","Search__enable_ai_eligibility_checks","DevPro__enable_developer_subscriptions","Profiles__enable_dashboard_curated_recommendations","MiscFeatureFlags__enable_variable_operator","TpcFeatures__enable_mirror_tenant_redirects","MiscFeatureFlags__enable_firebase_utm","CloudShell__cloud_shell_button","Search__enable_page_map","Cloud__enable_cloud_shell","BookNav__enable_tenant_cache_key","DevPro__enable_cloud_innovators_plus","MiscFeatureFlags__enable_view_transitions","MiscFeatureFlags__enable_dark_theme","Profiles__enable_developer_profiles_callout","MiscFeatureFlags__emergency_css","Profiles__enable_recognition_badges","Cloud__enable_cloud_shell_fte_user_flow","Profiles__enable_public_developer_profiles","Cloud__enable_free_trial_server_call","Cloud__enable_cloudx_ping","Experiments__reqs_query_experiments","MiscFeatureFlags__enable_project_variables","Concierge__enable_pushui","Profiles__enable_completecodelab_endpoint","Profiles__enable_page_saving","Cloud__enable_cloud_dlp_service","Search__enable_suggestions_from_borg","Profiles__enable_profile_collections","MiscFeatureFlags__developers_footer_dark_image","Cloud__enable_llm_concierge_chat","MiscFeatureFlags__developers_footer_image","Profiles__enable_awarding_url","Profiles__enable_release_notes_notifications","Cloud__enable_legacy_calculator_redirect","TpcFeatures__enable_required_headers","Cloud__enable_cloud_facet_chat"],null,null,"AIzaSyBLEMok-5suZ67qRPzx0qUtbnLmyT_kCVE","https://developerscontentserving-pa.googleapis.com","AIzaSyCM4QpTRSqP5qI4Dvjt4OAScIN8sOUlO-k","https://developerscontentsearch-pa.googleapis.com",2,4,null,"https://developerprofiles-pa.googleapis.com",[3,"android","Android Developers","developer.android.com",null,"android-dot-devsite-v2-prod.appspot.com",null,null,[null,1,null,null,null,null,null,null,null,null,null,[1],null,null,null,null,null,null,[1],[1,null,null,[1,20],"/recommendations"],null,null,null,[1,null,1],[1,1,null,1,1]],null,[18,null,null,null,null,null,"/images/lockup.svg","/images/touchicon-180.png",null,null,null,null,null,null,null,null,null,null,null,null,null,1,null,null,null,"/images/lockup-dark-theme.svg",[]],[],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[6,1,14,15,20,22,23,28,29,37,43],null,[[null,null,1],[1,1]],[[null,null,null,null,null,null,null,[["G-QFRN08RN6E"],null,null,[["G-QFRN08RN6E",1]]],null,null,null,null,1],null,[[2,2],[1,1]]],null,4,null,null,null,null,null,null,null,null,null,null,null,null,null,"android.devsite.google"],null,"pk_live_5170syrHvgGVmSx9sBrnWtA5luvk9BwnVcvIi7HizpwauFG96WedXsuXh790rtij9AmGllqPtMLfhe2RSwD6Pn38V00uBCydV4m"]') </script> <devsite-a11y-announce></devsite-a11y-announce> </body> </html>