Kotlinコルーチン完全ガイド2026:Android開発の非同期処理をマスターする
Android開発に必要なKotlinコルーチンの基礎から応用まで解説します。suspend関数、スコープ、ディスパッチャー、Flowまで体系的に学べる実践ガイドです。

Kotlinコルーチンは、Androidの非同期プログラミングを根本から変えました。コールバック地獄やAsyncTaskの時代は終わり、コルーチンを使えば非同期処理を同期的なコードのように記述でき、パフォーマンスと保守性を両立できます。
コルーチンは軽量で(1つのスレッドで数千個を実行可能)、ネイティブにキャンセル対応しており、JetpackをはじめとするAndroidエコシステムとシームレスに統合されています。
基礎を理解する
コードに入る前に、コルーチンがなぜ強力なのかを理解しましょう。
コルーチンとは何か
コルーチンは、中断可能な計算のインスタンスです。スレッドとは異なり、コルーチンはブロックしません。実行を中断し、そのスレッドを他のタスクに解放します。
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L) // Suspend without blocking
println("World!")
}
println("Hello,")
}
// Output: Hello, World!suspend関数:コルーチンの核心
suspendキーワードは、その関数がスレッドをブロックせずにコルーチンの実行を中断できることを示します。
suspend fun fetchUserData(userId: String): User {
return withContext(Dispatchers.IO) {
// Network call - runs on an IO thread
apiService.getUser(userId)
}
}
suspend fun fetchUserWithPosts(userId: String): UserWithPosts {
// Sequential execution
val user = fetchUserData(userId)
val posts = fetchUserPosts(userId)
return UserWithPosts(user, posts)
}基本ルール:suspend関数は、別のsuspend関数内、またはコルーチン内からのみ呼び出すことができます。
コルーチンスコープ
スコープはコルーチンのライフサイクルを定義します。メモリリークを防ぐために極めて重要な概念です。
viewModelScope:ViewModel用スコープ
class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
fun loadUser(userId: String) {
viewModelScope.launch {
_uiState.value = UserUiState.Loading
try {
val user = userRepository.getUser(userId)
_uiState.value = UserUiState.Success(user)
} catch (e: Exception) {
_uiState.value = UserUiState.Error(e.message)
}
}
}
}
sealed class UserUiState {
object Loading : UserUiState()
data class Success(val user: User) : UserUiState()
data class Error(val message: String?) : UserUiState()
}lifecycleScope:ActivityとFragment用スコープ
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
when (state) {
is UserUiState.Loading -> showLoading()
is UserUiState.Success -> showUser(state.user)
is UserUiState.Error -> showError(state.message)
}
}
}
}
}
}AndroidアプリケーションでGlobalScopeを使用してはいけません。GlobalScopeで起動されたコルーチンはライフサイクルに紐付かないため、メモリリークの原因になります。
ディスパッチャー:実行スレッドの制御
ディスパッチャーは、コルーチンがどのスレッドで実行されるかを決定します。
4つの主要ディスパッチャー
// Main: main thread (UI)
viewModelScope.launch(Dispatchers.Main) {
textView.text = "UI update"
}
// IO: I/O operations (network, database)
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchFromNetwork()
}
// Default: CPU-intensive computations
viewModelScope.launch(Dispatchers.Default) {
val result = heavyComputation(data)
}
// Unconfined: inherits caller context (rare use)withContext:ディスパッチャーの切り替え
class ImageProcessor {
suspend fun processImage(bitmap: Bitmap): Bitmap {
return withContext(Dispatchers.Default) {
// CPU-intensive processing on Default
applyFilters(bitmap)
}
}
suspend fun saveToGallery(bitmap: Bitmap) {
withContext(Dispatchers.IO) {
// Disk write on IO
saveToFile(bitmap)
}
}
}
// Usage in ViewModel
viewModelScope.launch {
val processed = imageProcessor.processImage(originalBitmap)
imageProcessor.saveToGallery(processed)
// Automatic return to Main for UI update
_uiState.value = UiState.Success(processed)
}async/awaitによる並列実行
タスクを並列に実行し、結果を統合するにはasyncを使用します。
suspend fun loadDashboard(): Dashboard {
return coroutineScope {
// Parallel launch
val userDeferred = async { userRepository.getUser() }
val statsDeferred = async { statsRepository.getStats() }
val notificationsDeferred = async { notificationRepository.getNotifications() }
// Await results
Dashboard(
user = userDeferred.await(),
stats = statsDeferred.await(),
notifications = notificationsDeferred.await()
)
}
}asyncを使えば、3つの呼び出しがすべて並列に実行されます。各呼び出しが1秒かかる場合、合計時間は逐次実行の3秒ではなく、約1秒になります。
エラーハンドリング
基本的なtry/catch
viewModelScope.launch {
try {
val user = userRepository.getUser(userId)
_uiState.value = UiState.Success(user)
} catch (e: HttpException) {
_uiState.value = UiState.Error("Server error: ${e.code()}")
} catch (e: IOException) {
_uiState.value = UiState.Error("Network error")
} catch (e: Exception) {
_uiState.value = UiState.Error("Unexpected error")
}
}CoroutineExceptionHandler
class UserViewModel : ViewModel() {
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
_uiState.value = UiState.Error(throwable.message)
Timber.e(throwable, "Error in coroutine")
}
fun loadUser(userId: String) {
viewModelScope.launch(exceptionHandler) {
val user = userRepository.getUser(userId)
_uiState.value = UiState.Success(user)
}
}
}Resultラッパーパターン
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
suspend fun <T> safeApiCall(apiCall: suspend () -> T): Result<T> {
return try {
Result.Success(apiCall())
} catch (e: Exception) {
Result.Error(e)
}
}
// Usage
class UserRepository(private val api: UserApi) {
suspend fun getUser(id: String): Result<User> = safeApiCall {
api.getUser(id)
}
}
// In ViewModel
viewModelScope.launch {
when (val result = userRepository.getUser(userId)) {
is Result.Success -> _uiState.value = UiState.Success(result.data)
is Result.Error -> _uiState.value = UiState.Error(result.exception.message)
}
}Androidの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
キャンセル処理:リソースの適切な解放
コルーチンは協調的キャンセルをサポートしています。リソースリークを防ぐために不可欠な仕組みです。
スコープによる自動キャンセル
class SearchViewModel : ViewModel() {
private var searchJob: Job? = null
fun search(query: String) {
// Cancel previous search
searchJob?.cancel()
searchJob = viewModelScope.launch {
delay(300) // Debounce
val results = searchRepository.search(query)
_searchResults.value = results
}
}
}キャンセル状態の確認
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
// Check if coroutine is cancelled
ensureActive()
processItem(item)
}
}
suspend fun downloadFiles(urls: List<String>) = coroutineScope {
urls.map { url ->
async {
try {
downloadFile(url)
} catch (e: CancellationException) {
cleanupPartialDownload(url)
throw e // Re-throw to propagate cancellation
}
}
}.awaitAll()
}CancellationExceptionを握りつぶしてはいけません:Exceptionをキャッチした場合は、キャンセルが正しく伝播するようにCancellationExceptionを再スローしてください。
Flow:リアクティブプログラミング
Flowは、コルーチン版のRxJava Observableです。より簡潔で、コルーチンとネイティブに統合されています。
Flowの作成と収集
fun getUsers(): Flow<List<User>> = flow {
while (true) {
val users = userApi.getUsers()
emit(users)
delay(5000) // Poll every 5 seconds
}
}
// Flow from Room
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllUsers(): Flow<List<User>>
}
// Collecting in ViewModel
viewModelScope.launch {
userDao.getAllUsers()
.catch { e -> _uiState.value = UiState.Error(e.message) }
.collect { users ->
_uiState.value = UiState.Success(users)
}
}StateFlowとSharedFlowの使い分け
class EventViewModel : ViewModel() {
// StateFlow: keeps last value, ideal for UI state
private val _uiState = MutableStateFlow(UiState.Initial)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// SharedFlow: for one-shot events (navigation, snackbar)
private val _events = MutableSharedFlow<UiEvent>()
val events: SharedFlow<UiEvent> = _events.asSharedFlow()
fun onButtonClick() {
viewModelScope.launch {
_events.emit(UiEvent.NavigateToDetail)
}
}
}
sealed class UiEvent {
object NavigateToDetail : UiEvent()
data class ShowSnackbar(val message: String) : UiEvent()
}主要なFlowオペレーター
userRepository.getUsers()
.map { users -> users.filter { it.isActive } }
.distinctUntilChanged()
.debounce(300)
.flatMapLatest { users ->
fetchUserDetails(users)
}
.catch { e ->
emit(emptyList())
}
.onEach { users ->
analytics.logUserCount(users.size)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)高度なパターン
指数バックオフによるリトライ
suspend fun <T> retryWithBackoff(
times: Int = 3,
initialDelay: Long = 100,
maxDelay: Long = 1000,
factor: Double = 2.0,
block: suspend () -> T
): T {
var currentDelay = initialDelay
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
Timber.w("Attempt ${attempt + 1} failed, retrying in ${currentDelay}ms")
}
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
return block()
}
// Usage
val user = retryWithBackoff {
userApi.getUser(userId)
}タイムアウト
suspend fun fetchWithTimeout() {
try {
val result = withTimeout(5000L) {
api.fetchData()
}
processResult(result)
} catch (e: TimeoutCancellationException) {
showError("Request took too long")
}
}
// Or with a default value
val result = withTimeoutOrNull(5000L) {
api.fetchData()
} ?: defaultValueまとめ
Kotlinコルーチンは、現代のAndroid開発に欠かせない技術です。Jetpackエコシステムと完全に統合された、高性能な非同期プログラミングの手法を提供します。
チェックリスト
- メモリリーク防止のために
viewModelScopeとlifecycleScopeを使用する - 適切なDispatcherを選択する(Main、IO、Default)
- try/catchまたはResultラッパーでエラーを処理する
- 協調的キャンセルを実装する
- リアクティブなデータストリームにはFlowを使用する
- UI状態にはStateFlow、イベントにはSharedFlowを選択する
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
コルーチンをマスターすることで、Androidプロジェクトと技術面接の両方で大きなアドバンテージを得ることができます。日々の実践を重ね、高度なパターンにも積極的に取り組んでみてください。
タグ
共有
関連記事

Kotlin 2.3 Android面接対策:名前ベースの分割代入、KMP、頻出質問を徹底解説【2026年版】
2026年のAndroid開発者面接で問われるKotlin 2.3の新機能を網羅的に解説。名前ベースの分割代入、Kotlin Multiplatform、コンテキストパラメータ、Flowとコルーチンのコード例付き。

Jetpack Compose: 高度なアニメーションを段階的に
Compose の高度なアニメーションを完全解説:遷移、AnimatedVisibility、Animatable、ジェスチャー、滑らかな Android インターフェイスのためのパフォーマンス。

Jetpack Compose面接質問20選(2026年版)
Jetpack Composeの面接で頻出する20の質問を解説。リコンポジション、状態管理、副作用、ナビゲーション、パフォーマンス最適化、アーキテクチャパターンを網羅。