これまで私たちのメモアプリは、すべてのデータがスマホの中だけで完結していました。Day 2の今日は、このアプリをインターネットの世界に接続し、クラウド上のサーバーと通信するための「神経系(ネットワーキング層)」を構築します。
このステップをマスターすれば、あなたは「クライアント・サーバー型」アプリ開発者として大きな一歩を踏み出すことになります。
今回は、サーバー開発の手間を省くため、偽物のサーバーを簡単に作れるMockAPIというサービスを「練習相手」として利用します。
1. 今回のゴール (要件定義)
- 目的: Androidアプリが、リモートサーバー(MockAPI)と通信するための、再利用可能で堅牢なネットワーキング層を構築する。
-
スコープ:
- ⭕ やること: Retrofitの導入、API仕様の定義、Hiltによる依存性注入、そして最初の通信テストまで。
- ❌ やらないこと: この通信機能を実際のUIに反映させること(それはDay 3以降のタスクです)。
2. 全体像の設計 (基本設計)
-
アーキテクチャ: 既存のMVVMアーキテクチャを拡張し、
Repositoryが新設するApiServiceを通じてサーバーと通信できるようにします。 -
コンポーネント:
-
networkパッケージを新設し、通信関連のコードを集約します。 -
DTO (Data Transfer Object): サーバーとの通信専用のデータクラス(例:
MemoResponse)を作成します。 - ApiService: サーバーのAPIエンドポイント(URL)と通信方法(GET, POSTなど)を定義するインターフェースです。
-
Hiltモジュール:
RetrofitやApiServiceといった、アプリ全体で共有する通信部品の作り方をHiltに教えます。
-
3. 詳細設計 & 実装ステップ
ここからは、具体的なコードと共に実装手順を詳細に解説します。
Step 1: サーバーの準備 (MockAPIのセットアップ) (約30分)
🎯 目標: 通信の相手となる、練習用のサーバー(API)を用意する。
👨💻 作業内容:
- MockAPI.io にアクセスし、アカウントを作成します。
-
userリソースの作成:-
Resource Name:user -
Schema:email(String),password(String) を追加してCreate。
-
-
memoリソースの作成:-
Resource Name:memo -
Schema:title(String),content(String),createdAt(Date),updatedAt(Date),userId(String) を追加してCreate。
-
-
APIのURLを控える:
- 画面上部に表示されている
https://xxxxxxxx.mockapi.io/api/v1/のようなURLをコピーしておきます。
- 画面上部に表示されている
Step 2: 依存関係の追加 (Gradle設定) (約30分)
🎯 目標: サーバーと通信するためのライブラリ「Retrofit」をプロジェクトに追加する。
👨💻 作業内容:
app/build.gradle.ktsを開き、dependenciesブロックに以下のライブラリを追記し、「Sync Now」をクリックします。
app/build.gradle.kts
dependencies {
// ... (HiltやComposeの依存関係) ...
// ★★★ 以下を追記 ★★★
// Retrofit (本体)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// kotlinx.serialization と Retrofit を連携させるためのコンバータ
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
// 通信の詳細なログを見るためのインターセプタ (デバッグに超便利)
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
}
Step 3: API仕様の定義 (通信のルール作り) (約1.5時間)
🎯 目標: MockAPIの仕様に合わせて、通信用のデータクラス(DTO)と、通信メソッド(エンドポイント)を定義したインターフェースを作成する。
👨💻 作業内容:
-
com.example.simplememoapp_androidの下にnetworkパッケージを、さらにその下にdtoパッケージを新設します。 -
dtoパッケージにMemoResponse.ktを作成します。 -
networkパッケージにApiService.ktを作成します。
network/dto/MemoResponse.kt (新規作成)
package com.example.simplememoapp_android.network.dto
import kotlinx.serialization.Serializable
@Serializable
data class MemoResponse(
val id: String,
val createdAt: String,
val title: String,
val content: String,
val updatedAt: String,
val userId: String
)
【ポイント】: これはDB用のMemoクラスとは別物です。サーバーとの通信専用のデータ形式なので、dto (Data Transfer Object) と呼びます。
network/ApiService.kt (新規作成)
package com.example.simplememoapp_android.network
import com.example.simplememoapp_android.network.dto.MemoResponse
import retrofit2.http.GET
import retrofit2.http.Path
interface ApiService {
// 例: .../users/{userId}/memos というURLにGETリクエストを送る
@GET("users/{userId}/memos")
suspend fun getMemosForUser(@Path("userId") userId: String): List<MemoResponse>
// 今後、ログイン用のPOSTや、メモ作成用のPOSTなどもここに追加していく
}
Step 4: Hiltモジュールの拡張 (DIの設定) (約30分)
🎯 目標: Hiltに「Retrofitの作り方」を教え、アプリのどこからでも使えるようにする。
👨💻 作業内容:
di/AppModule.ktを開き、RetrofitとApiServiceのインスタンスを生成するための@Provides関数を追記します。
di/AppModule.kt (追記)
// ... import文を追加 ...
import com.example.simplememoapp_android.network.ApiService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// ... (Database, Dao, RepositoryのProvides関数) ...
// ★★★ 以下を追記 ★★★
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
// 通信のログをLogcatに出力するための設定
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
val contentType = "application/json".toMediaType()
// kotlinx.serializationがJsonのパースで厳格すぎるとエラーになる場合があるため、設定を緩める
val json = Json { ignoreUnknownKeys = true }
return Retrofit.Builder()
.baseUrl("https://xxxxxxxx.mockapi.io/api/v1/") // ★ Step1で控えたあなたのURLに書き換える!
.client(okHttpClient)
.addConverterFactory(json.asConverterFactory(contentType))
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
【注意】
baseUrlには、Step 1で控えたあなた専用のMockAPIのURLを必ず設定してください。
Step 5: 動作確認 (最初の通信テスト) (約30分)
🎯 目標: UIはまだないが、バックエンドとの神経系が正しく繋がったかをLogcatで確認する。
👨💻 作業内容:
-
MemoRepositoryとMemoListViewModelに一時的なテストコードを埋め込みます。
di/AppModule.kt (一時的な修正)
// ...
@Provides
@Singleton
// ★引数にapiServiceを追加
fun provideMemoRepository(dao: MemoDao, apiService: ApiService): MemoRepository {
// ★apiServiceを渡す
return MemoRepository(dao, apiService)
}
data/repository/MemoRepository.kt (一時的な修正)
// ★コンストラクタにapiServiceを追加
class MemoRepository(
private val memoDao: MemoDao,
private val apiService: ApiService
) {
// ...
// ★テスト用メソッドを追加
suspend fun checkApiStatus() {
apiService.getMemosForUser("1")
}
}
ui/viewmodel/MemoListViewModel.kt (一時的な修正)
// ...
import android.util.Log
@HiltViewModel
class MemoListViewModel @Inject constructor(
private val repository: MemoRepository
) : ViewModel() {
// ...
init {
// ★テスト用のコードをinitブロックに追加
viewModelScope.launch {
try {
Log.d("APITEST", "APIへの接続テストを開始します...")
repository.checkApiStatus()
Log.d("APITEST", "APIへの接続に成功しました!")
} catch (e: Exception) {
Log.e("APITEST", "APIへの接続に失敗しました: ${e.message}")
}
}
}
// ...
}
-
アプリを実行し、Android StudioのLogcatで
APITESTと検索します。APIへの接続に成功しました!と表示され、その前にOkHttpのログ(JSONデータなど)が出力されていれば、今日のタスクは完全成功です。 -
【重要】 動作確認が終わったら、
MemoListViewModelのinitブロックに追加したテストコードは必ず削除してください。
お疲れ様でした!これでDay 2のタスクは完了です。あなたのアプリは、サーバーと会話する能力を手に入れました。