Open34

【アウトプット✍️】Android開発

YusukeYusuke

Kotlin Coroutines

Coroutinesとは?

軽量のスレッドのようなもので、処理同士を並列に実行できる。非同期処理を実装するのに使用する。

Androidでの使用例

例えば、APIから商品の情報を取得してUIに反映するという処理。これを非同期処理にしないとレスポンスが返ってくるまでスレッドをブロックするのでANRが発生したりするのでやってはいけない。そして使用するのがCoroutine。

Coroutine Scopeとは?

コルーチンを実行できる有効範囲。Androidでは以下のScopeをよく使う

ViewModel Scope

ViewModelでコルーチン使うときのScopeでViewModelが破棄されると自動的にキャンセルされる。

LifecycleScope

FragmentやActivityなどandroidx.lifecycleを持っているものに使うScope。そのままlaunchしてコルーチンを開始すると、FragmentやActivityがON_DESTROYのイベントを受け取ったらコルーチンも自動でキャンセルされる。(Activity/Fragmentだと onDestroy)ただlaunchWhenResumedとかメソッドが用意されており、特定のライフサイクルの状態が始まったときに何か処理をしたいという需要に応えることができる。ただ最近ではrepeatOnLifecycleを使用する方が一般的になっています。(バックグラウンドに移動した際に完全にCoroutinesをキャンセルしてくれるのでリソース削減の面から)

https://at-sushi.work/blog/35/

Dispathersとは?

Coroutineが実行されるスレッドを指定できる。Dispatchers.Mainはメインスレッドで実行できる。Dispachers.IOはネットワーク通信などIO操作を処理する際にバックグラウンドスレッドで実行できたりする。Dispachers.Defaultは、CPUに負荷がかかる処理を実行するときに使う。どでかリストの並び替えとか。

launchとAsyncの違い

両方とも子ルーチンをスタートさせる「Coroutine Builder」

launch

coroutine内の処理の結果を返さないコルーチンを開始する。戻り値はJob。「Fire and Forget」の処理はこちらを使う。launchのブロック内で発生した例外を処理しないとクラッシュする。

async

任意の単一の結果を返すコルーチンを開始する。戻り値はDeferredで任意の型のインスタンスを後で返すことができるオブジェクト。await関数を使用することで結果を受け取ることができる。3つAPIリクエストを並列で実行して、結果が返ってきたらローディングを消したいみたいなときに使える。syncはawaitを実行したら例外が発生してた場合、クラッシュする。

Coroutineの例外処理について

CoroutineExceptionHandlerか、try-catchを使用して例外を処理する。

関連

非同期処理

あるタスクを実行している最中に、その処理を止めることなく別のタスクを実行する処理

YusukeYusuke

Coroutine Flowとは

コルーチンの1種で、ワンショット系のデータを処理というよりか、ストリーム系のデータを処理するときに使用する。例えば、データベースからリアルタイムで更新情報を受け取るような処理に使えたりする。RoomのDAOもFlowを返せたりする。RepositoryでFlowを公開しておくと、どこかでFlowの中身を更新すれば、collectしている場所で最新の値を取得できるようになる。

cold flow

  • 誰かがcollectするまでflowの処理が動かない
  • consumerごとに新しいフローを作成する
  • 値をstoreできない

hot flow

  • 誰かがcollectしなくてもflowの処理が動く
  • 複数のconsumerが1つのフロー(Single Source of truth)
  • 値をstoreすることができる

SharedFlow

hot flow.hot flowの特性にもあるように、複数のconsumerでデータや状態を共有することができる。例えば、複数のUIコンポーネントで、Single Source of Truthの原則を守りつつ共通のデータを使うことができる。sharedFlowを作成する方法の1つとしてMutableSharedFlowを使うことができる。

MutableSharedFlow

MutableなのでFlowの値を変更可能なSharedFlow。replayにnを指定した場合、subscribeした際、過去n回の値を受信することができる。extraBufferCapacityはemitされた値を格納しておくバッファのサイズを指定する。Subscriberの処理が長い場合、バッファに空きがない場合emitがsuspendされてしまう。バッファを指定しておくとemitがsuspendされずに済む。onBufflerOverflowにはバッファに空きがない状態でemitされた場合の挙動を設定できる。SUSPENDならsuspendする。DROP_LATESTならemitが破棄される。DROP_OLDESTならバッファのデータの中で一番古いデータを破棄し、空いたバッファに値を保持する。

参考

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-shared-flow.html

https://engawapg.net/kotlin/1963/sharedflow/

StateFlow

hot flow. 状態保持用の特別なsharedFlow(repeat=1, DROP_OLDEST)。初期値が必須で、subscribeすると直近の値が1件ながれてくる。つまりsubscribeしたら最新の値を取得できる。さらにバッファには一番最新でemitした値が保持される。また同じ値のemitは無視される。このような性質からAndroid開発ではUiStateなどに使用されることが多い(UIはアプリを起動した時点で何らかの画面を表示しなければならないので、初期値をもつStateFlowは相性がいい。さらに、値が変化した時だけUIを更新できるので表示更新の負荷を下げることができる)

関連

DAO

DBにアクセスしデータを取得などの機能を持ったオブジェクトで、DAOで定義するメソッドはSQLのクエリに自動でマッピングされる

YusukeYusuke

@Jvm〇〇アノテーション

@JvmStatic

JavaからKotlinのCompanion objectブロックの定数やメソッドを参照する際に使用することが多い。このアノテーションをつけることで、JavaからJavaのstatic変数やメソッドを使用する要領で呼び出すことができる。

@JvmOverloads

JavaからKotlinのデフォルト値を持つクラス、または関数を呼び出せるようにするアノテーション。
https://qiita.com/yass97/items/1582308681833d28db58

@JvmField

Javaからget/setメソッド不要で直接プロパティとしてアクセスすることができる。

YusukeYusuke

インライン関数

inline 関数

inline関数内のコードを呼び出し元に展開することができる。関数にラムダをたくさん渡す場合などに使用することでパフォーマンスが向上する可能性がある。(高階関数のパフォーマンス向上)

https://at-sushi.work/blog/47/

YusukeYusuke

lateinit vs lazy

lateinit

宣言時に変数を初期化せず、後に初期化する。これによりnullで初期化してnullableなオブジェクトを作るみたいな必要がなくなる。

lazy

クラス生成時などにインスタンス生成のオーバーヘッドが大きい場合などに、lazyを使用すると、必要になったときに初期化する。(クラス生成時にかかるコストが減らせる)

比較表

lateinit lazy
var/valキーワード var val
nullについて non-null non-null, nullable
プリミティブ型 使えない 使える
用途 インスタンス生成時にフィールドの値を確定できない場合に使用 重い処理を必要になるまで遅延させたい場合に使用

https://4engineer.net/kotlin/lateinit_vs_bylazy

YusukeYusuke

クラスの初期化

// プライマリコンストラクタ
class A(name: String) {
     // セカンダリコンストラクタ
    constructor(foo: String, bar: String): this(foo) {
        // 初期化処理
    } 

    init {
        // 初期化処理
    }
}

プライマリコンストラクター

  • コンストラクタの引数を指定する
  • 引数で初期化されるプロパティを定義する

セカンダリコンストラクター

  • プライマリコンストラクター以外のコンストラクター
  • Kotlinで複数のコンストラクタを持つクラスは一般的ではない。引数にデフォルト値を使えるので

init

初期化ブロック。クラスが生成された時に実行される初期化コードを記述する。初期化処理をかけないプライマリーコンストラクタと一緒に使われることが多い。引数をゴニョゴニョしたりなど。

https://blog.y-yuki.net/entry/2019/05/25/093000

YusukeYusuke

const val とvalどう使い分ける?

基本的にはconstはつけられる場合はつけた方が良い。コンパイル時定数になるので一般的にオーバーヘッドは少なくなる。valで宣言するとgetterメソッドを介して値にアクセスする、const valで宣言すると直接値にアクセスできる。companion objectのブロック内など使えるところではconstを使うが吉

https://www.wdic.org/w/TECH/const (Kotlin)

YusukeYusuke

高階関数とlambda式

高階関数

関数の引数に関数をとる、もしくは関数を返す関数のこと。

lambda式

無名関数を簡潔に記述できるもの。Kotlinでは下記のような感じ。

test { value -> value * value} // lambda式

fun test(a: Int, f: (Int) -> Int) {
  aa = f.invoke(a)
}

YusukeYusuke

Companion object

クラス内に作成されるSingleton。クラスのインスタンスを作成しなくても、メソッドや変数にアクセスできるので、staticなフィールドやメソッドを定義する場合に使用する

YusukeYusuke

What is Context? How is it used?

アプリケーションのグローバル情報へアクセスするためのインタフェースです。グローバルな情報とはファイルリソース、パーミッションなど。Contextとは抽象クラスであり、ActivityやApplicationのSuper Classになっている。
主にApplication ContextとActivity Contextがある。

Application Context

Application全体のライフサイクルに依存する。leakCanaryなどのライブラリの初期化でContextを使いたいなどに使用。

Activity Context

Activityのライフサイクルに依存する。そのためActivityが破棄される時にContextも破棄しないとメモリリークが発生するので注意。ありがちなアンチパターンとしては、ViewModel生成時にActivityのContextを渡してしまってメモリリークが発生したみたいな。AlertDialogなど出すときはActivity ContextでないとActivityのテーマが正しく当たらない場合がある。

その他

Intent生成時のContextはどうする?

正直どちらでも良い。なぜならパッケージ名(String)の取得にしか使われていないから。公式ではActivity Contextを使用している場合が多いのでActivityを使うが良いかも。
https://zenn.dev/yass97/articles/2ea2641a6823f9

参考

https://ytrino.hatenablog.com/entry/2016/05/26/033936
http://yuki312.blogspot.com/2012/02/thisgetapplicationcontextactivityapplic.html

YusukeYusuke

What is AndroidManifest.xml?

Androidアプリケーションの情報をAndroidのビルドツール、OS、GooglePlayに提供するために必ず必要なファイル。ActivityやServiceなどのアプリコンポーネントの宣言、Intent filter、パーミッションなどの情報を記述する。

YusukeYusuke

What is Application class?

Androidアプリが起動した際に最初にインスタンス化されるクラス。アプリ起動時に実行したい初期設定やライブラリの設定(Hiltのコード生成のトリガー)などをする。

YusukeYusuke

What is Activity and its lifecycle?

(誤解を恐れて言うと)Androidアプリの画面を表す。ライフサイクルと呼ばれる開始から終了までの状態遷移をもつ。

lifecycleメソッド

onCreate

システムが最初にActivityを作成したときに呼び出される。Activityの初期化やUI要素のセットアップなど、一度だけ行う必要がある処理を実行します。例えばAndroidView時代はここでfindViewByIdみたいなことをしていた。savedInstanceStateを用いてInstanceStateの復元なども行う

onStart

Activityをユーザに見せる直前に呼び出される(Activityの遷移で元に戻った時も呼ばれる)。Activityが表示されてユーザーの操作を受け付ける状態になる際の準備処理を担当します。uiStateのcollectとかはこの状態になったら行う。

onResume

Activityが前面に表示されユーザと対話可能になる直前に呼び出される。別Activityに遷移などしない限りは、基本的にはこの状態にとどまる。

onPause

ユーザーがActivityと相互作用しなくなったが、画面上にはまだ表示されているときに呼び出される(例えば別のActivityに遷移するときなど)。カメラ機能の停止などバックグラウンドに行ったら停止した方が良い処理はここに書く。onPause() の実行は非常に短時間で終了し、保存操作を実行するのに十分な時間を確保できない場合があります。このため、アプリデータまたはユーザーデータの保存、ネットワーク呼び出し、データベース トランザクションの実行には onPause() を使用せずonStop()を使用する。

onStop

Activityがユーザから見えなくなったときに呼び出される。上記が起こるのは、アクティビティが破棄される場合と、別のアクティビティに隠される場合がある。onPause()での説明の通り、CPU に比較的高い負荷をかけるシャットダウン操作を実行するには、onStop() を使用する。

onDestroy

Activityが破棄(finish()や画面の回転など)される直前に呼び出される。onCreateで取得したリソースを解放するのが一般的

参考

https://developer.android.com/reference/android/app/Activity

https://woshidan.hatenablog.com/entry/2016/04/30/210612

https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ja

YusukeYusuke

Why do we need to call setContentView() in onCreate() of Activity class?

まずsetContentView()は画面に表示するレイアウトファイル(xml)を指定するメソッド。これをonCreate()で行わなければいけない理由は、onStart()やonResume()複数回呼ばれる可能性があるから。そのたびにsetContentView()を呼ぶのは非効率。onStart()onResume()は例えば別のActivityに遷移してバックキーで戻ってきたら呼ばれたりする。

最近ではComposeの登場によりこのメソッドは使わないことが多い印象。

YusukeYusuke

What is onSavedInstanceState() and onRestoreInstanceState() in activity?

onSaveInstanceState()

Configuration changeなどが発生しActivityがkillされるときに、値を保存したいときに使うコールバックメソッド。Bundleに保存する。

onRestoreInstanceState()

onSaveInstanceStateで保存した値を復元するために使用される。onCreate()でも保存した値を取り出すこともできる。このメソッドが呼び出されるのは、アクティビティを再作成する場合のみ

https://developer.android.com/reference/android/app/Activity#onRestoreInstanceState(android.os.Bundle)

YusukeYusuke

What is Fragment and its lifecycle.

Activityで使用できる「サブアクティビティ」のようなもの。Activityに複数のFragmentを組み合わせてマルチペインUIを実現できたり、複数のActivityでFragmentを再利用できる。もともとはタブレットなどの大画面でのよりダイナミックで柔軟な UI デザインに対応するために導入された。

lifecycleメソッド

Activityと同様にライフサイクルが存在する。
altテキスト

onAttach

FragmentをActivityに追加するときに呼ばれる。

onCreateView

Fragmentが初めてUIを描画するタイミングで呼び出される。inflate()メソッドを用いてxmlのレイアウトファイルを読み込む処理を記述する。

https://developer.android.com/guide/components/fragments?hl=ja

YusukeYusuke

What are "launchMode"?

Activityの起動方法について設定する。

前提

タスク

タスクは複数のアクティビティのまとまりのこと。タスクは一つのアプリ内のActivityをまとめたものとは限らない。アプリが異なっても、同じタスクに所属する場合がある。

アフィニティ

Androidアプリケーション内の異なるActivityがどのタスクに属するかを指定することができる。

standard

デフォルト。システムは常にターゲットのタスクにActivityの新しいインスタンスを作成し、そのインスタンスにIntentを渡す。

singleTop

Activityのインスタンスがターゲットのタスクの一番上に存在する場合、既にあるインスタンスにIntentを渡す。

singleTask

Activityのインスタンスが存在しない場合は、新しいタスクにActivityが生成されそこにintentが飛ばされる。存在する場合はそれ以外のActivityをfinish()した上で、そのonNewInstance()を呼び出しActivityがルートにくる。

https://qiita.com/kskso9/items/1ee5b1dbe996402ae94b
https://zenn.dev/dd_sho/articles/2dcee31a829bed
https://developer.android.com/guide/topics/manifest/activity-element?hl=ja

YusukeYusuke

What is the difference between a Fragment and an Activity? Explain the relationship between the two.

Activityはアプリの画面を管理するコンポーネント。FragmentはActivity内で再利用可能なUI部品を表すコンポーネントです。複数のFragmentを1つのActivity内で組み合わせて使用することもできる。(大画面端末など)

YusukeYusuke

What is the difference between adding/replacing fragment in backstack?

add

ActivityにFragmentを追加するメソッド。画面遷移に add() を使用した場合、遷移前の Fragmentに重ねて表示されるため、別途remove()する必要あり。

remove

指定したFragmentを削除するときメソッド。 削除されたFragmentはActivityとの関連付けが解除されFragmentのライフサイクルのonDetach()まで呼ばれる(addToBackStack() されている場合には Activity との関連付け解除が行われず、 onDestroyView()まで呼ばれる)

replace

addとremoveを同時に行うメソッド。画面遷移をしたい場合に用いる。addToBackStack()を一緒に呼ぶことが多い

https://nein37.hatenablog.com/entry/2018/05/06/204943

YusukeYusuke

What is the purpose of addToBackStack() while commiting fragment transaction?

遷移元のFragmentバックスタックに積む。これによりバックキーを押したときに遷移元のFragmentに遷移できる。

(replace()を呼んでaddToBackStack()を呼ぶと、遷移元のFragmentのライフサイクルはonDestryoViewまで呼ばれる)

YusukeYusuke

What is View in Android?

UIコンポーネントの基本単位でButtonやImageViewなどコンポーネントもViewを継承して作られている。タップなどのイベント処理や継承による独自のViewの定義などもできる。

YusukeYusuke

Difference between View.GONE and View.INVISIBLE?

View.GONE

Viewを非表示にするための定数。要素が非表示になると、その要素が占めていたスペースも消失します。つまり、非表示になったViewは他の要素に対して影響を及ぼさなくなる。

View.INVISIBLE

Viewを不可視にするための定数。要素は画面上に表示されていないが、その要素が占めているスペースは依然として確保されたまま。つまり、不可視になったViewは他の要素に対して影響を及ぼす可能性があります。

YusukeYusuke

Can you a create custom view? How?

ViewやViewGroupを継承して、レイアウトファイルでレイアウトを組んだり、onDraw()をoverrideしてcanvasでガリガリ図形を描いたりする。

YusukeYusuke

What are ViewGroups and how they are different from the Views?

ViewはUI要素の基本構成要素でButton、EditText、CheckBoxなど。ViewGroupはViewやViewGroupを子に持つコンテナ。LinearLayouやConstraintLayoutなどがある。

YusukeYusuke

What is a Canvas?

2Dグラフィックを描画するために用いられる。Viewを継承してCustom Viewを作成してonDraw()とかでガリガリ図形を描画することができる。

YusukeYusuke

What is a SurfaceView?

高パフォーマンスでアニメーション描画などを行うためのビューコンポーネントです。通常のViewとは異なりUIスレッドとは別のスレッドで描画を行うことができるため、滑らかなアニメーションを実現ことができます。

YusukeYusuke

Tell about Constraint Layout

UI要素同士の相対的な位置を制約(コンストレイント)を使用して指定してViewやViewGroupを配置する。

YusukeYusuke

What is the difference between ListView and RecyclerView?

How does RecyclerView work internally?