Androidアプリ開発の極意 4,5章ユーザー体験と非同期処理

前の記事同様に Androidアプリ開発の極意 | 技術評論社の「ユーザーストレス軽減」まわりの実装Tipsと、並行処理の選択肢をざっくり整理してExpo周りと絡めたもの。細かい実装には踏み込まず、要点だけ。 はじめに アプリが固まる原因の大半は、メインスレッド(UIスレッド)を長時間ブロックしていること。非同期処理の設計は結局「ユーザーを待たせない」という一点のためにある。 二部構成で一部は本を読んだ感想+α、二部がExpoだとどうなるかって話。 基本的にAIで出して、それを読んで調べたりしたもの。 第1部:Androidの話 Android(Java/Kotlin)側だけで完結する話。UXまわりの実装Tipsと、非同期処理の手段がsynchronizedからVirtual Threadsまでどう変遷してきたかを、Expoの話を挟まずに整理する。 UXまわりの小さな判断 時間のかかる処理にはProgressBarで進捗を見せる SharedPreferencesは永続化のためディスクI/Oが発生する セルラー通信時は重い処理の前に一言断りを入れると親切 エラーはその場で表示しつつ、Crashlytics/ACRAのような開発者向けツールで後から追える形が理想 NDKは高速化と引き換えにデバッグの複雑さが増す。基本的には奥の手 非同期処理はなぜ増え続けているのか synchronizedは「同時に入らない」ことしか保証しない。順番までは保証しない。 Threadを直接生成した場合も同様で、生成順と実行順は一致しない。順番を保証したいならExecutors.newSingleThreadExecutor()のような直列化の仕組みを使う。 ExecutorServiceは、スレッドを直接管理せず、プールとキューで管理する仕組み。スレッド数の制御や再利用をアプリ側のロジックから切り離せる。 AsyncTaskはAPI 30で非推奨になった。ライフサイクル管理が弱く、Activity/Fragment破棄後もタスクが動き続けてリークやクラッシュを招くのが理由。 今の主流はKotlin Coroutines。スレッドをブロックせず中断できる軽量な並行処理で、排他制御が必要ならMutexを使う。UIに紐づく短い処理はCoroutines、画面を離れても続けるべき処理はWorkManagerと使い分ける。 JVM側ではJDK 21でVirtual Threads(JEP 444)が正式機能になった。I/Oバウンドな処理を大量にさばくのに向くが、CPUバウンドには向かない。ただしこれはJDK/Java SE側の機能で、Android Runtime(ART)は非対応。Androidアプリでそのまま使えるわけではない。 Akkaはアクター単位でメッセージパッシングする設計思想。2022年にライセンスがApache 2.0からBSL(Business Source License)に変わり、2024年10月以降はproduction利用にライセンスキーが必須になった。個人・OSS・startupは無料キー、商用は原則サブスクという体系。汎用ツールというより大規模分散システム向けの専門ツールという位置づけに変わっている。 比較表(Android) 手段 排他制御 順序保証 位置づけ synchronized あり なし 低レベルの排他制御プリミティブ Thread直接生成 なし なし 基本的には避ける ExecutorService 設計次第 直列化すれば可 CPUバウンド処理で有効 AsyncTask なし なし API 30で非推奨 Kotlin Coroutines Mutexで対応 設計次第 Android/Kotlinの主流 Virtual Threads 従来通り 設計次第 JVM側、I/Oバウンド向け(ART非対応) Akka アクターモデル 条件付き 大規模分散システム向け 「排他制御」と「順序保証」は別物。ここを混同すると設計は破綻する。AsyncTaskはAPI 30で非推奨、今はCoroutines(短命処理)とWorkManager(永続処理)を使い分けるのが主流。Virtual Threads(JDK 21)はJVM側の話でARTは非対応。 ...

August 18, 2026 · 1 min

Go言語でネットワークプログラミングを学ぶ - 第2章

2.1 プロジェクト構造 go-network-programming/ ├── go.mod ├── go.sum ├── main.go ├── packet.go ├── node.go ├── link.go ├── network_stats.go # 新規追加 └── bandwidth_limiter.go # 新規追加 この章では、ネットワークに時間の概念を本格的に導入します。実際のネットワークのように、帯域幅制限、パケット処理時間、スループット測定を実装し、大きなファイルの送信をシミュレートします。 2.2 ネットワーク統計の追加 ネットワークの性能を測定するための統計機能を追加します。 ファイル名: ./network_stats.go package main import ( "fmt" "sync" "time" ) // NetworkStats はネットワークの統計情報を管理する // 実際のネットワークモニタリングツールのような機能を提供 type NetworkStats struct { mu sync.RWMutex startTime time.Time totalPacketsSent int64 totalPacketsRecv int64 totalBytesSent int64 totalBytesRecv int64 packetLossCount int64 lastUpdateTime time.Time } // NewNetworkStats は新しい統計オブジェクトを作成 func NewNetworkStats() *NetworkStats { return &NetworkStats{ startTime: time.Now(), lastUpdateTime: time.Now(), } } // RecordSentPacket は送信パケットを記録 func (ns *NetworkStats) RecordSentPacket(packet *Packet) { ns.mu.Lock() defer ns.mu.Unlock() ns.totalPacketsSent++ ns.totalBytesSent += int64(packet.Size) ns.lastUpdateTime = time.Now() } // RecordReceivedPacket は受信パケットを記録 func (ns *NetworkStats) RecordReceivedPacket(packet *Packet) { ns.mu.Lock() defer ns.mu.Unlock() ns.totalPacketsRecv++ ns.totalBytesRecv += int64(packet.Size) ns.lastUpdateTime = time.Now() } // RecordPacketLoss はパケット損失を記録 func (ns *NetworkStats) RecordPacketLoss() { ns.mu.Lock() defer ns.mu.Unlock() ns.packetLossCount++ ns.lastUpdateTime = time.Now() } // GetThroughput は現在のスループットを計算(bps: bits per second) func (ns *NetworkStats) GetThroughput() float64 { ns.mu.RLock() defer ns.mu.RUnlock() duration := time.Since(ns.startTime).Seconds() if duration == 0 { return 0 } // バイト数をビット数に変換(1バイト = 8ビット) totalBits := float64(ns.totalBytesSent) * 8 return totalBits / duration } // GetPacketLossRate はパケット損失率を計算(0.0-1.0) func (ns *NetworkStats) GetPacketLossRate() float64 { ns.mu.RLock() defer ns.mu.RUnlock() if ns.totalPacketsSent == 0 { return 0.0 } return float64(ns.packetLossCount) / float64(ns.totalPacketsSent) } // Print は統計情報を表示 func (ns *NetworkStats) Print() { ns.mu.RLock() defer ns.mu.RUnlock() duration := time.Since(ns.startTime) throughputBps := ns.GetThroughput() throughputKbps := throughputBps / 1000 lossRate := ns.GetPacketLossRate() * 100 fmt.Printf("=== Network Statistics ===\n") fmt.Printf("Duration: %v\n", duration.Round(time.Millisecond)) fmt.Printf("Packets Sent: %d\n", ns.totalPacketsSent) fmt.Printf("Packets Received: %d\n", ns.totalPacketsRecv) fmt.Printf("Bytes Sent: %d\n", ns.totalBytesSent) fmt.Printf("Bytes Received: %d\n", ns.totalBytesRecv) fmt.Printf("Throughput: %.2f Kbps\n", throughputKbps) fmt.Printf("Packet Loss Rate: %.2f%%\n", lossRate) fmt.Printf("=========================\n") } 2.3 帯域幅制限機能の実装 実際のネットワークのように、帯域幅制限を実装します。 ...

January 11, 2026 · 7 min