デモアプリのラマ銀行をandroidで作る
ラマ銀行 https://qiita.com/tnagata/items/471ff499b3bd9e9eff33
のバックエンドをそのままし、フロントエンドをandroidアプリにする
開発環境: Windows 11 Home, Android Studio Otter 2025.2.1
実行方法:バックエンドはaxum079フォルダでcargo run(ダウンロード方法などは上記の記事を参照)
フロントエンドはエミュレータ上で動かす(詳しくは下記参照)
アプリの実行イメージ
右側のweb版から25000円送金して、左側のandroid版で入金が確認できる
アプリの仕様概略
- ホームページ: 新規口座開設(登録)とログインボタン
- 口座登録: ユーザー名、パスワード、氏名、口座番号(アルファベット1文字 + 4~6桁数字)、初期残高を入力
- ログイン: ユーザー名とパスワードで認証。失敗時はエラーメッセージを表示
- ログイン後の機能: 残高表示(初期ページ)、送金処理、取引履歴、登録情報、本人確認写真のアップロード
- 以下のボタンで各ページに遷移する
- 送金: 送金先口座番号と金額を入力。残高不足時は警告を表示
- 残高
- 履歴: 過去1ヶ月の入金・送金履歴を表示
- プロファイル パスワードの変更
- アップロード 本人写真のアップロード
📂 ソースファイルの役割・機能(1行要約)
①アプリ基盤・エントリポイント
- MainActivity.kt: アプリのエントリポイントで、Jetpack
Composeを用いた画面遷移(Navigation)の管理と依存関係の注入を行う - NetBankApplication.kt: アプリケーション全体のライフサイクルと初期設定を管理する基盤クラス
②データ層 (Data Layer)
ローカル保存 (Local) - data/local/AuthPreferences.kt:
SharedPreferencesを使用し、JWTトークンや口座番号などのログイン情報をデバイス内に安全に保存・管理する
データモデル (Model)
- data/model/AccountModels.kt: 残高照会レスポンスや取引明細(Transaction)などの口座関連のデータ構造を定義する
- data/model/AuthModels.kt: ログインや新規登録リクエスト、それに対するレスポンスのデータ構造を定義する
- data/model/LocalDateTimeSerializer.kt: APIのISO 8601形式の日時文字列をKotlinの LocalDateTimeへ変換するためのシリアライザー
- data/model/ProfileResponse.kt: プロファイル画像パスを含むユーザー情報のレスポンスを定義する
- data/model/TransferRequest.kt: 送金元・先、金額、メモといった送金処理に必要な情報を保持するデータクラス
- data/model/UploadResponse.kt: 画像アップロード成功時のメッセージやファイルパスを受け取るためのモデル
リモート通信 (Remote)
- data/remote/AccountService.kt:
Ktorクライアントを用い、残高取得・履歴照会・送金・画像アップロード等のAPI通信を実装する - data/remote/AuthService.kt: ログインおよび新規口座開設のAPIエンドポイントへのリクエストを担当
- data/remote/HttpRoutes.kt:
Rust(Axum)バックエンドのベースURLや各APIエンドポイントのパスを一括管理する定数クラス - data/remote/NetworkModule.kt: Ktor
HttpClientの初期化(JSONシリアライズやログ設定)と、各サービスのシングルトン提供を行う - data/remote/UserService.kt: パスワード変更など、ユーザー設定に関連するAPI通信を担当
- data/remote/model/ChangePasswordRequest.kt: パスワード変更時にバックエンドへ送信するデータを定義する
リポジトリ (Repository)
- data/repository/AuthRepository.kt: AuthService
をラップし、UI層に対して認証関連のビジネスロジックを抽象化して提供する - data/repository/UserRepository.kt: UserService をラップし、ユーザー情報の変更操作などをリポジトリとして管理する
③ UI層 (UI Layer)
画面 (Screens)
- ui/BalanceScreen.kt: ユーザーの現在の残高を円単位で表示し、メインメニューとして機能する画面
- ui/HistoryScreen.kt: 取引履歴を時系列にリスト表示し、入出金の詳細を確認できる画面
- ui/HomePage.kt: アプリ起動時に表示されるトップ画面で、ログインや口座開設のナビゲーションを提供する
- ui/LoginScreen.kt: ユーザー名とパスワードを入力して認証を行い、成功時にトップ画面へ遷移させるUI
- ui/ProfileScreen.kt: パスワード変更などのユーザーセキュリティ設定を行うための専用画面
- ui/RegisterScreen.kt: 新規ユーザー登録と初期入金を行い、新しく口座を開設するための入力フォーム画面
- ui/TransferScreen.kt: 他の口座への送金機能を提供し、金額やメモを指定して実行する画面
- ui/UploadScreen.kt: 本人確認用の画像を選択・アップロードし、プロファイル画像を更新する機能を提供する
UI共通コンポーネント・テーマ
- ui/BottomNavigationBar.kt: 主要画面間の移動とログアウト機能を提供する、アプリ共通の下部ナビゲーションバー
- ui/theme/Color.kt / Theme.kt / Type.kt: Material Design
3に基づいたアプリ全体の配色、ダークモード、タイポグラフィの設定を行う
④ 状態管理 (ViewModel)
- ui/viewmodel/RegisterViewModel.kt:
口座開設画面の状態(入力中・通信中・成功・エラー)を管理し、非同期処理を制御する
🔄 処理フローの詳細解説を2例
①フロントエンドからバックエンド、データベースを経てJWTが返されるまでのログイン処理のフローを解説する:
(1) フロントエンド:ユーザー入力と送信
- LoginScreen (ui/LoginScreen.kt)
- Button onClick:
ユーザーが「ログイン」ボタンをタップすると、coroutineScope.launch(コルーチン)により非同期通信が開始される。UIをフリーズさせずにバックグラウンドで処理を行う
- Button onClick:
- AuthService (data/remote/AuthService.kt)
- loginメソッド: 通信ライブラリ Ktor HTTP Client を使用し、入力された username と password を LoginRequest (JSON)
としてサーバーへPOST送信する - ポイント: 接続先URLはエミュレータからPC本体を指す http://10.0.2.2:3001 を使用する
- loginメソッド: 通信ライブラリ Ktor HTTP Client を使用し、入力された username と password を LoginRequest (JSON)
(2) バックエンド:リクエストの受信とルーティング
- create_app / auth_routes (axum079/src/lib.rs)
- ルーティング定義により、Androidアプリからの POST /auth/login リクエストが handlers::auth::login
関数にルーティングされる
- ルーティング定義により、Androidアプリからの POST /auth/login リクエストが handlers::auth::login
(3) バックエンド:認証処理(DB照合とパスワード検証)
- login ハンドラ (axum079/src/handlers/auth.rs)
- DB照合: sqlx::query_as を使用して、SQLiteデータベース(usersテーブル)からユーザー名に一致するレコードを検索する
- パスワード検証: bcrypt::verify
関数を使い、アプリから送られた平文パスワードと、DBに保存されているハッシュ化されたパスワードを照合する
(4) バックエンド:JWT生成とレスポンス送信
- login ハンドラ (続き)
- Claimsの作成: ユーザーIDと有効期限(24時間後)を含む Claims 構造体を作成する
- JWT生成: jsonwebtoken::encode 関数を使用し、サーバー側の秘密鍵で署名してJWT(トークン文字列)を生成する
- レスポンス送信: 生成されたトークンとユーザー情報を LoginResponse構造体に詰め込み、JSON形式でフロントエンド(Androidアプリ)に返す
(5) フロントエンド:JWTの受信と永続化
- AuthPreferences (data/local/AuthPreferences.kt)
- データの永続化: 受信したJWTトークンをAndroid標準の SharedPreferencesに保存する。これにより、アプリを終了しても次回起動時にログイン状態を維持できる
- Navigation (MainActivity.kt)
- 画面遷移: ログイン成功のコールバックを受け取り、navControllerを用いて「残高照会画面(BalanceScreen)」へ遷移する
AndroidとWebアプリ(Dioxus)版との対比する:
- 非同期処理: Rust の async/await に相当する機能として、Kotlin では Coroutines (コルーチン) を採用。suspend
関数を組み合わせることで、Rust 同様に同期処理のような見た目で非同期通信を安全に記述できる - 通信ライブラリ: Rust の reqwest や surf の代わりに、Kotlin マルチプラットフォームでも定評のある Ktor HTTP Client
を採用している - セキュアな保存: Dioxus(Web/Desktop)で設定ファイルや localStorage に保存していた情報の代わりに、Android
OSが提供する SharedPreferences を利用して認証情報を管理している
② 取引履歴表示ついて、フロントエンドからDB、そして画面にデータが表示されるまでのフローを解説する:
(1) フロントエンド:リソースの要求
- HistoryScreen (ui/HistoryScreen.kt)
- LaunchedEffect: Dioxusの use_resourceに相当する機能。画面表示時にコルーチンを起動し、非同期で取得を開始する
- AuthPreferences: LocalStorage の代わりに、Androidの SharedPreferencesからログイン中の「自分の口座番号」と「JWTトークン」を読み出す
- AccountService (data/remote/AccountService.kt)
- 読み出したトークンを HTTPヘッダーの Authorization: Bearer <トークン> にセットし、/api/history/<口座番号>
へGETリクエストを送信する
- 読み出したトークンを HTTPヘッダーの Authorization: Bearer <トークン> にセットし、/api/history/<口座番号>
(2) バックエンド:認証とルーティング
- AuthUser ミドルウェア (axum079/src/middleware/auth.rs)
- Androidアプリから送られてきたヘッダーのJWTを検証する。有効であればリクエストを続行させ、無効であればエラーを返す
- get_history ハンドラ (axum079/src/handlers/transactions.rs)
- ルーティングにより、認証済みのリクエストがこのハンドラへ到達する
(3) バックエンド:SQLクエリの実行
- SQLxによる検索
- sqlx::query_as を使用し、SQLiteの transactions テーブルに対してクエリを実行する
- 条件: from_account(送金元)または
to_account(送金先)が、引数で渡された自分の口座番号と一致する全レコードを抽出する - 日時: DBには日時は UTC(協定世界時) で保存されており、そのままの形で取得される
(4) バックエンド:レスポンス送信
- JSONシリアライズ
- 取得した取引データのリストをJSON形式に変換し、フロントエンド(Androidアプリ)へレスポンスとして返す
(5) フロントエンド:パースと日本時間への変換
ここが Android 版での最大の特徴で、
- LocalDateTimeSerializer (data/model/LocalDateTimeSerializer.kt)
- JSON内の日時文字列(UTC)を Kotlin の LocalDateTime オブジェクトへデシリアライズする
- TransactionItem (ui/HistoryScreen.kt)
- 日本時間への変換: バックエンドから届いた UTC の時間を、ユーザーが住む 日本時間(JST) へと UI表示の直前に変換する
val formattedDateTime = transaction.created_at
.toInstant(TimeZone.UTC)
// UTCの「Instant」として認識
.toLocalDateTime(TimeZone.of("Asia/Tokyo"))
// 日本のタイムゾーンに変換
.toJavaLocalDateTime()
// 整形用にJava形式へ
.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"))
// 文字列に整形
-
- 表示の最適化: 自分の口座番号と比較し、送金(赤)か入金(緑)かを判定してアイコンや色を出し分け、LazyColumnでリスト表示する
技術的なポイント
- バックエンドの共通化: 前回のWebアプリ(Dioxus)と同じ Rust コード、同じミドルウェア、同じ SQL クエリが Android
アプリでもそのまま機能している - フロントエンドの責務:
バックエンドはあくまで不変のデータ(UTC)を返し、フロントエンドがユーザーのコンテキスト(端末のタイムゾーン)に合わせて表示を調整するという、モバイル開発のセオリー通りの実装になっている - ライブラリの使い分け: Rust の chrono で行っていた処理を、Kotlin では kotlinx-datetime で実現している
🚀 試してみたい方へ
GitHub リポジトリ: https://github.com/tnagata/llamabank-rust
- リポジトリをクローン
-
README.mdの指示に従ってbackend(axum079)を起動 -
esato / esato123でログインして動作確認! - 次に
- 新規登録も試して!
実機での検証
最後に先月購入したandroidタブレット Xiaomi Pad7でも検証した。
ipアドレスをプライベートなアドレスに書き換えるだけ、と思いきやAndroid OS (v16)の強固なセキュリティにより、Cleartextの送信が許可されない。manifestファイルに許可を書いてもダメで、ソースコードを変更することになった。


