0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android/Flask】macOS環境でKotlin (Compose) + Retrofit2 と Python (Flask) + PostgreSQL を連携した読書管理アプリの構築記

0
Posted at

概要

macOS 上で Python (Flask) + PostgreSQL 16 による REST API バックエンドを構築し、Android (Kotlin / Jetpack Compose) から Retrofit2 を用いて CRUD 処理を行う読書管理アプリ(BookLogApp)を実装しました。

開発過程で遭遇した macOS ファイアウォール越しでのエミュレータ通信の安定化 や SocketTimeoutException の回避ノウハウ についてもまとめています。


開発環境・使用技術

バックエンド (Mac)

  • OS: macOS
  • Language: Python 3.12
  • Framework: Flask
  • Database: PostgreSQL 16
  • API Endpoints:
    • GET /api/books - 書籍一覧の取得
    • POST /api/books - 新規書籍の登録
    • PUT /api/books/<id> - 書籍情報の更新
    • DELETE /api/books/<id> - 書籍の削除

フロントエンド (Android)

  • IDE: Android Studio
  • Language: Kotlin
  • UI: Jetpack Compose (Material3, LazyColumn, Scaffold, AlertDialog)
  • Architecture: MVVM (ViewModel + StateFlow)
  • HTTP Client: Retrofit 2 + Gson + OkHttpClient (タイムアウト 30秒設定)
  • Asynchronous: Kotlin Coroutines

主なはまりどころと解決策

1. エミュレータ ➔ Mac ローカルサーバーへの接続(10.0.2.2 vs ローカル IP)

  • 現象: エミュレータの Chrome ブラウザで 10.0.2.2:5000 は開けるものの、アプリ内の Retrofit 通信で SocketTimeoutException が発生。
  • 原因: macOS のセキュリティ制限やソケット再利用時のキャッシュ、およびローカルネットワークルーティングの影響。
  • 解決策:
    1. Mac のローカル IP アドレス(例: 192.168.0.5)を確認し、BASE_URL に :5000/ を明示指定(http://192.168.0.5:5000/)。
    2. OkHttpClient で connectTimeout / readTimeout を 30 秒に明示設定。

2. macOS ファイアウォール ON 時の Connection reset 解消

  • 現象: ファイアウォールを ON にすると、ローカル IP 接続時に SocketException: Connection reset が発生する。
  • 解決策:
    1. macOS 15 (Sequoia) の「システム設定」➔「ネットワーク」➔「ファイアウォール」➔「オプション」を開く。
    2. 個別許可リスト(+ボタン)に、サーバーを実行している 「ターミナル(Terminal / IDE)」 および python3 バイナリ を直接追加し、「着信接続を許可」に設定。

実装したコードのポイント

1. Retrofit クライアント設定 (RetrofitInstance.kt)

タイムアウト時間を 30 秒に延ばし、接続を安定化させています。

package com.example.booklogapp

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object RetrofitInstance {
    private const val BASE_URL = "[http://192.168.0.5:5000/](http://192.168.0.5:5000/)"

    private val okHttpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS)
        .build()

    val api: BookApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(BookApiService::class.java)
    }
}

2. ViewModel での非同期 State 管理 (BookViewModel.kt)

StateFlow を用いて、POST / PUT / DELETE 成功時に自動で一覧(fetchBooks)を最新化しています。

fun addBook(newBook: Book, onSuccess: () -> Unit) {
    viewModelScope.launch {
        _isLoading.value = true
        try {
            val response = RetrofitInstance.api.createBook(newBook)
            if (response.isSuccessful) {
                onSuccess()
                fetchBooks() // 一覧を自動再取得
            }
        } catch (e: Exception) {
            _errorMessage.value = e.localizedMessage
        } finally {
            _isLoading.value = false
        }
    }
}

まとめと今後の展望

バックエンドの PostgreSQL データベースから Android アプリの Jetpack Compose 画面まで、リアルタイムに双方向同期するアプリ基盤が完成しました。

次回は以下のアプリアップグレードに取り組む予定です!

  • タイトル・著者名による キーワード検索 および ステータス(未読/読書中/読了)フィルター

  • Jetpack Compose テーマカラーやカードデザインの刷新

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?