自分の作ったアプリに、機種変更しても安心なバックアップ機能があったら、素敵だと思いませんか?
今回は、私たちが開発しているメモアプリに「全件エクスポート機能」を追加します。この機能実装を通して、Android開発の必須スキルであるKotlinコルーチンを実践的に学び、「UIを止めない」快適なアプリの作り方をマスターしましょう!
1. 今回作るもの (要件定義)
まずは、何を作るのかを明確にします。
- やりたいこと: ユーザーがボタンを一つ押すだけで、アプリ内の全てのメモを一つのファイルとしてスマホ内に保存できるようにする。
-
目的:
- ユーザーにデータのバックアップ手段を提供する。
- 開発者として、コルーチンを使った非同期でのファイル書き出し処理を学ぶ。
機能のスコープ
- ⭕ 今回やること: 全メモをJSON形式のファイルとして書き出す機能。
- ❌ 今回やらないこと: ファイルからメモを復元するインポート機能、個別のメモを書き出す機能。
2. どうやって作るか? (基本設計)
「ファイルの書き出し」は、メモの件数が多いと時間がかかる重い処理です。もしこれをメインスレッドで実行すると、アプリがフリーズしてしまいます。
そこで、Kotlinコルーチンの出番です!コルーチンを使って、この重い処理を裏側(バックグラウンドスレッド)で安全に実行する設計を考えましょう。
設計の登場人物と役割
今回の実装には、4人の重要な専門家が登場します。彼らの役割を理解することが、コルーチンを使いこなす鍵です。
① ActivityResultLauncher
(ピザのネット注文係 🍕)
- 役割: カメラやファイル選択など、別のアプリを呼び出して、その結果を受け取るための現代的な専門家。
- 今回の仕事: ユーザーに「どこにファイルを保存しますか?」と尋ねるためのファイル選択画面を起動し、ユーザーが選んだ保存場所の情報(許可証)を受け取ります。
② ContentResolver
(図書館の司書 📚)
- 役割: スマホ内の様々なデータ(連絡先、ファイルなど)に、安全にアクセスするための総合受付。
-
今回の仕事:
ActivityResultLauncher
が受け取った**「ファイルの場所への許可証(Uri
)」を提示することで、その場所に実際にファイルを書き込むための道具(OutputStream
)**を貸し出してくれます。
③ viewModelScope.launch
(賢いアシスタント 👨💼)
- 役割: UIを止めない非同期処理を開始するための決まり文句。
- 今回の仕事: ユーザーが「エクスポート」ボタンを押したら、賢いアシスタントを呼び出します。このアシスタントが、これから説明するファイル書き出しの一連の処理を担当してくれます。ユーザーが画面を閉じるなどしてViewModelが不要になったら、アシスタントは自動で作業を中断してくれるため、非常に安全です。
④ withContext(Dispatchers.IO)
(裏方専門スタッフへの依頼 📦)
- 役割: コルーチンの中で、仕事場所(スレッド)を一時的に切り替えるための命令。
-
今回の仕事: アシスタント(
viewModelScope.launch
)は、withContext(Dispatchers.IO)
を使って、ファイル書き出しという力仕事(I/O処理)を、その道のプロである裏方専門スタッフに任せます。その間、アシスタントはフリーズすることなく、他のことができます。作業が終わると、裏方スタッフは結果をアシスタントに報告し、アシスタントはメインの仕事場に戻ってきます。
3. 実装の全体像
これらの専門家が、どのように連携して機能を実現するのか見ていきましょう。
UI層 (MemoListScreen
)
-
TopAppBar
に「全件エクスポート」メニューを追加します。 -
ActivityResultLauncher
(ピザの注文係)をあらかじめ登録しておきます。「ファイルが選ばれたら、ViewModel
のexportMemos
メソッドを呼んでね」と予約しておきます。 - ユーザーがメニューをタップしたら、予約しておいた
ActivityResultLauncher
の注文ボタンを押して、ファイル選択画面を起動します。
ViewModel層 (MemoListViewModel
)
- UIから
exportMemos(uri)
が呼び出されたら、viewModelScope.launch
(賢いアシスタント)で非同期処理を開始します。 - アシスタントは、
withContext(Dispatchers.IO)
(裏方スタッフへの依頼)を使って、Repositoryのファイル書き出し処理を呼び出します。 - 処理が完了したら、メインスレッドに戻り、結果(成功/失敗)をSnackbarでUIに通知します。
MemoListViewModel
のコードイメージ
fun exportMemos(uri: Uri, contentResolver: ContentResolver) {
// 1. 賢いアシスタントを雇う
viewModelScope.launch {
try {
// 2. 裏方スタッフに重い仕事を任せ、完了を待つ
val resultMessage = repository.exportMemosToFile(uri, contentResolver)
// 3. 仕事の結果をUIに通知する
_eventFlow.emit(UiEvent.ShowSnackbar(resultMessage))
} catch (e: Exception) {
_eventFlow.emit(UiEvent.ShowSnackbar("エラーが発生しました"))
}
}
}
データ層 (MemoRepository
)
-
exportMemosToFile
メソッドをsuspend
関数(コルーチンの中で呼び出せる関数)として定義します。 - このメソッドの中で、
withContext(Dispatchers.IO)
を呼び出し、実際の処理をバックグラウンドスレッドに切り替えます。 -
withContext
ブロックの中で、以下の処理を実行します。- DBから全メモデータを取得する。
- 取得したデータをJSON形式の文字列に変換する(
kotlinx.serialization
ライブラリが便利です)。 -
ContentResolver
(図書館の司書)を使って、指定されたUri
にJSON文字列を書き込む。 - 成功メッセージを返す。
MemoRepository
のコードイメージ
suspend fun exportMemosToFile(uri: Uri, contentResolver: ContentResolver): String {
// withContextで、このブロック内だけバックグラウンド(IO)スレッドに切り替える
return withContext(Dispatchers.IO) {
// DBから全メモを取得
val memos = memoDao.getAllMemos().first()
// JSONに変換
val jsonString = Json.encodeToString(memos)
// ファイルに書き込み
contentResolver.openOutputStream(uri)?.use { outputStream ->
outputStream.write(jsonString.toByteArray())
}
"エクスポートが完了しました!"
}
}
まとめ
今回は、「全件エクスポート機能」を題材に、Kotlinコルーチンを使った非同期処理の実装方法を設計しました。
-
ActivityResultLauncher
でOSの機能を安全に呼び出し、 -
ContentResolver
を通じてファイルにアクセスし、 -
viewModelScope.launch
でUIを止めない処理を開始し、 -
withContext(Dispatchers.IO)
で重いI/O処理をバックグラウンドに任せる。
この一連の流れをマスターすれば、あなたのアプリは格段に快適で、プロフェッショナルなものになります。ぜひ、この設計を元に実装に挑戦してみてください!