Skip to content
sho10case
Go back

AndroidエンジニアのためのGemini API実装・逆引きガイド【安全・高速・切れにくい実装】

こんにちは、ショウです!

最近、AndroidアプリにGemini API(Google AI SDK)を組み込むプロジェクトが増えていますね。しかし、ドキュメント通りに実装したはずなのに「APIキーの公開が怖い」「回答中に画面を回すと消える」「画像送信が遅すぎる」といった、モバイル特有の課題に直面することも多いのではないでしょうか。

今回は、僕が実際に複数のアプリにGeminiを組み込む中で得た知見をもとに、Android開発者が踏みがちな落とし穴とその解決策を逆引き形式でまとめました。


【逆引き1】APIキーをGitHubに上げたくない!

最も多いのが、apiKey = "AIza..." とコードに直書きしてしまうミスです。これはセキュリティ上の重大なリスクです。

解決策:secrets-gradle-plugin を使う

Googleが推奨している secrets-gradle-plugin を使い、local.properties で管理するのが正解です。

  1. local.properties にキーを記述:
    GEMINI_API_KEY=YOUR_API_KEY_HERE
  2. build.gradle (Module) で参照:
    val apiKey = BuildConfig.GEMINI_API_KEY

これで、Gitにはキーを含めず、ビルド時に安全に注入できます。


【逆引き2】回答が途中で止まる!画面回転で消える!

Geminiの回答(ストリーミング)を受け取っている最中にスマホを横にしたり、別のActivityに移ったりすると、処理がキャンセルされてしまうことがあります。

解決策:ViewModelScope と Flow を組み合わせる

ActivityFragment で直接 collect するのではなく、ViewModel 内でライフサイクルを管理します。

// ViewModel内での実装例
private val _uiState = MutableStateFlow("")
val uiState: StateFlow<String> = _uiState

fun generateContent(prompt: String) {
    viewModelScope.launch {
        generativeModel.generateContentStream(prompt).collect { chunk ->
            _uiState.value += chunk.text
        }
    }
}

viewModelScope を使うことで、画面回転(Configuration Change)が起きても処理が継続され、UIの状態も保持されます。


【逆引き3】文字入力中にアプリがカクつく!

APIを叩く処理がメインスレッド(UIスレッド)をブロックしている可能性があります。

解決策:Dispatchers.IO を明示する

SDK内部でも非同期処理は行われますが、リクエストの準備や大きな画像データの処理などは、明示的にIOスレッドで行うのが安全です。

withContext(Dispatchers.IO) {
    val response = generativeModel.generateContent(prompt)
    // 結果をメインスレッドに反映
}

【逆引き4】画像を送ると「タイムアウト」や「遅延」がひどい

最新のスマホカメラで撮った写真は数MB〜数十MBあります。これをそのままGeminiに送ると、アップロードに時間がかかるだけでなく、トークン消費も激しくなります。

結論:送信前に「リサイズ」と「圧縮」を必須にする

Gemini 1.5 Pro/Flashともに、画像は長辺が一定以上あれば十分に認識可能です。

fun resizeBitmap(source: Bitmap): Bitmap {
    val maxSize = 768 // この程度で十分高精度に認識されます
    val width = source.width
    val height = source.height
    val ratio = width.toFloat() / height.toFloat()
    
    val targetWidth = if (ratio > 1) maxSize else (maxSize * ratio).toInt()
    val targetHeight = if (ratio > 1) (maxSize / ratio).toInt() else maxSize
    
    return Bitmap.createScaledBitmap(source, targetWidth, targetHeight, true)
}

これを噛ませるだけで、レスポンス速度が劇的に向上します。


まとめ:AI実装は「モバイルの作法」で

Gemini APIは強力ですが、Androidアプリとして公開する以上、**「セキュリティ」「ライフサイクル」「パフォーマンス」**という3つの壁は避けて通れません。

今回の逆引きガイドが、皆さんのアプリ開発の一助になれば幸いです。

それでは、また!



Related Posts