概要
今週のAndroidWeeklyという週報に紹介されている記事のまとめ。
https://androidweekly.net/issues/issue-532
皆さんの最新技術キャッチアップの助けに少しでもなれば幸いです。
Domain-Specific Models
API通信におけるデータクラスをDomain-Specificに分けることを奨励した記事。
@DatabaseTable(tableName = "boards")
data class Board(
@DatabaseField(columnName = “name”)
@SerializedName("BoardName")
private var boardName: String?,
private var cards: List<Card>?,
private var lastViewedTime: Long?
)
このように一つのクラスを
- APIとの通信
- Local DBとのやりとり
- UIからのリクエスト
で使いまわしているとデータのmutability/immutability
やnull/non-null
がごっちゃになって使いづらい。
そこで以下の3つのクラスに分けることを奨励している。
ApiModels : API構造に合わせたモデル。immutable & nullable
DbModels : DB構造に合わせたモデル。mutable(これはDBの種類による) & non-null
UiModels : UIの要求に合わせたモデル。immutable & non-null
How to fix common Android API deprecations
新しいAndroidADKリリースの際に発生するメジャーなdeprecationの対策を紹介
FragmentでのMenu API
ActionBarにメニューを追加する方法のアップグレード
Before
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
setHasOptionsMenu(true)
}
After
class MyFragment : Fragment(), MenuProvider {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
val menuHost: MenuHost = requireActivity() as MenuHost
menuHost.addMenuProvider(
this, viewLifecycleOwner, State.RESUMED
)
}
}
// あとはMenuProvider interfaceに必要なmethodをoverrideするだけ
Handler constructor
processMessage & Runnable object
をAndroidに送るためのHandlerの使い方のアップグレード
Before
private val handler = Handler()
After
private val handler = Handler(Looper.getMainLooper())
startActivityForResult APIs
startActivityForResult
はほかのActivityを起動し、result
を取得するメソッドで写真を撮ったりパーミッションを取得する際に使われる。
Before
startActivityForResult(
Intents.editProfile(activity, false, null, memberId),
EDIT_PROFILE
)
// ここで結果を取得
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == EDIT_PROFILE && resultCode == RESULT_OK) {
onRefresh()
}
}
After
// このようにlauncherを使うスタイルが奨励されている
private val editProfileLauncher = registerForActivityResult(StartActivityForResult()) {
result: ActivityResult ->
if (result.resultCode == RESULT_OK) {
onRefresh()
}
}
editProfileLauncher.launch(Intents.editProfile(activity, false, null, memberId))
Sparking Jetpack Compose at Tinder
TinderがどのようにComposeをアプリへ導入したかについての記事。
まずcomposeがalphaの段階では以下のようなものを作ったりし、チームでcomposeの知見を深めていったそうです。
そしてKotlin, Android Studio, Android Gradle Plugin, AndroidXなどのbuildシステムを調査しまずはユーザーの目に留まりにくい画面に実装することで安定性を調査しました。
その後MaterialThemeの調査を経てComposeをメインとする開発にシフトしていきました。従来のAndroid Viewと比べてコードの再利用性が決め手の一つになったようです。
Testing Room database with Coroutines and Flows - Testing Fundamentals
Flowを使ったRoom databaseをテストする方法を紹介した記事。ポイントとしては
- Flowをテストするために
Turbine
を使う - @RunWithでJUnitの代わりにAndroidJUnit4を使う
-
Room.inMemoryDatabaseBuilder
で仮想のデータベースを作る - test coroutine内でテストを行うために
runTest{}
内でテストを行う
などです
Custom Layouts, measuring policies, and BoxWithConstraints in Jetpack Compose
全てのCompose(Box, Column, Row...)の元となるLayout
を使って自由にComposeを作るやり方を紹介している記事。
Constraint(制約)を親から子に渡していくことでUIを作っていくようです。ここら辺を使いこなせると自由にComposeが作れそう。
Object-oriented or functional? Two ways to see the world
OOP(オブジェクト指向)、FP(関数型プログラム)の違いについて触れている記事。まず
- OOPは世界をobjectの集まりとみる考え方
- FPは世界をactionの集まりとみる考え方
というのが筆者の主張。例えばベッドルームに関して、OOPではベッド、ランプ...がある部屋みたいになるけどFPだと寝る、くつろぐ、洗濯物をかけるなどができるという表現をする。
コードの例としてはこんな感じ
// OOP
class Paybill(
//...
) {
fun pay(): PayedPaybill = …
//...
}
// FP
fun bestStudents(semester: String) = repo
.findStudentsInSemestet(semester)
.map(Student::id)
.map(repo::findUser)
.filter(::isPassing)
.sortedByDescending(Student::result)
多くの人がOOPを使ってコードを書いていると思っているが実際はかなり入り混じったものを書いている。
例としてInterface
がある
interface UserRepository {
fun findUser(userId: Int): User
fun addUser(user: User)
fun deleteUser(userId: Int)
}
これはよく見る実装だが、上で述べたようにactionの集まりを定義している、つまり考え方としてはとてもFPに近いもの。
最後にまとめとしてそれぞれの考え方の利点を述べている
OOP
- XMLやComposeなどのUIの定義
- データベース
- ネットワーク接続
FP
- Rxなどで使われている複雑なtransformation
- ビジネスロジックの実装
まとめとしては以上。オブジェクト指向の本をよく見かけるけど関数型思考に関する本を読むことも読んだほうがいいのかもと思いました。
To Flow or not to Flow? Message subscription in Kotlin
CallbackとKotlin Flowをメッセージサブスクリプションを例として説明している記事。
まずCallbackを使う場合はこんな感じ
fun MyStructure.onChange(block: MyStructure.(Key) -> Unit)
myStructure.onChange{ key ->
println(get(key))
}
Flowの場合はこんな感じで、scopeについて考えたり少し手間
val MyStructure.changes: Flow<Key>
myStructure.changes.onEach{ key ->
println(myStructure.get(key)
}.launchIn(scope)
ただsubscribeを解除する場合Callbackは
// Callback
fun MyStructure.onChange(owner: Any, block: MyStructure.(Key) -> Unit)
fun MyStructure.removeChangeListener(owner: Any)
このようになり、手動で解除しなければいけないがFlowは自動で管理されるので気にしなくていい。
Unpacking Android Security: Part 3 — Insecure Communication
モバイルデバイスへのセキュリティ脅威TOP10に関する記事のPart3、今回は第三位の安全でないコミュニケーションについて書いている。
まとめとして
- 必ず通信ではHTTPSを使う
- ユーザー証明書を盲目的に信用してはならない
- 現在特定のサーバーとの通信を保証するためにCertificate pinningという技術が使われているが、CT(ceretificate transparency)という証明書のパブリックキーを使わない技術を導入したほうが安全
- プロダクションアプリでは必ずログを消そう
- 最後に通信を行うコードに関して自分で考えてみる
MVI with state-machine. Basics.
MVI アーキテクチャパターンについての記事。まずMVIとは
- Model : データを保持し、変更をViewに伝える
- View : UI eventをIntentとしてModelに伝える
- Intent : ユーザーの行動をmodel内でのデータのとして表す
利点としては
- Single source of truth
- 単方向データフロー
- 全体のテストが可能
- JetpackComposeとの親和性
欠点としては
- 簡単なfunctionも冗長になりやすい
- Reducerロジックの多用
- 学習コスト
簡単にまとめると以下の画像にあるStateMachineを使い、状態の変化によってScreenの表示などを変えるよう、個人的にはかなりよさそうなアーキテクチャ
Inspecting Performance
パフォーマンスの監視は三段階に分けられる
- Passive : 単純にLogcatを見たりアプリの速度を目視で確認したりすること
- Manual : Profilerを使いCPU, Memory, Network使用率などを確認
- Automated : Jetpack Macrobenchmark libraryなどを使い自動でアプリ起動などのパフォーマンスを測る
Jetpack Compose Accompanist — An FAQ.
AccompanistというJetpackComposeをサポートするライブラリ群がある。
まず前提としてAndroidXではモジュールを超えてExperimental APIを使うことができない、なので compose のnavigation transition
などを複数モジュールで扱うことができない。
その問題をAccompanistを使うことで解決できる、つまりテスト段階のcompose libraryを自由に使えるようサポートするのが役目。
プロダクションアプリに使うことも可能なようだがバージョン管理には気を付けたほうがよさそう。
Droidcon NYC iOS app with Compose
Droidconというイベントの公式アプリでCompose UIを採用した際の話