新しいノートブック

Android大好き

umeda_apk #4 メモ

2018/06/07 にCAさんの大阪支社で開催された umeda.apk #4 - Report from Google I/O 2018 に関するメモです

shibuya-apk.connpass.com

What's new in Android @hi6484 / 株式会社メルカリ

Google I/O 2018 で発表された内容の全体像

Android App Bundles

  • Legacy APK
    • リソースはすべて入れる
  • Dynamic Delivery
    • 必要なリソースだけ入れる

Android Jetpack

  • サポートライブラリが変わった
  • Android KTX (Kotlin Extension)
  • etc...

Battery

  • Adaptive Battery

    • 消費電力をアプリの使用状況に合わせて調整する
  • App Standby Buckets

    • 4つの状態がある
      • Active
      • Working set
      • Frequent
      • Rare
    • 制限されるのは
      • Job
      • Alarms
      • High Priority Firebase Cloud Message
      • Network

Background Input & Privacy

  • マイク、カメラ、センサー情報
    • バックグラウンドで使わない!

Location

Security

  • Fingerprint ManagerがDeprecated

↓↓↓

Enterpise

企業向けの機能 (回転寿司にある注文用のタブレットアプリとかああいう向けのやつ)

  • android.content.pm.CrossProfile
    • Profileをスイッチ
  • DevicePolicyManager.setLockTaskPackage()
  • DevicePolicyManager.createAndManagerUser(MAKE_USER_EPHEMERAL)
    • ユーザを作成する

Notification

ユーザがメッセージを受信したときに新しいAPIを追加

  • Inline images/stickers
  • Participant images
  • Smart reply UI
    • RemoteInput.setChoices()

Deprecated Policy

  • 2018/08
    • 新しいアプリはTarget API 26以上
  • 2018/11
    • アプリの更新はTarget API 26以上
  • 2019/09
    • 64bit対応 (32bit Optional)

App Compatibility

privateと@hideのAPIを使わない!

g.co/dev/appcompatへリクエストして、APIに対して3タイプのリストを持っている

  • Blacklist
  • Dark Greylist
  • Light Greylist

Camera

  • タイムスタンプ
  • Stabilization (手ブレ補正)
  • Display Flash
  • USBカメラ
  • マルチカメラ

ImageDecoder

  • Animated Image Drawable (GIFファイルをロードできる)
  • BitmapFactoryは使わずImageDecoderつかってね

Media

  • HDRをサポート

    • HDRビデオ再生できるようにVP9サポート
  • High Effeiciency Image Format(HEIF)

    • HEVC/H.256コーデック
    • .heif とか .heic

Chrome OS

Preview Android Studio on Chomebooks


Android Jetpack @satorufujiwara / 株式会社サイバーエージェント

Android Jetpack とは...

  • 良いアプリを早く簡単につくるための、ライブラリやツールや設計指針
  • Foundation(基本), Archtecture(アプリ設計), Behavior(動作), UI の4つに分類されている
  • Support LibraryやArchtecture Commponentも含まれる

What's new in Android Jetpack

今回更新があったもの

  • WorkManager
  • Navigation
  • Paging
  • Slices
  • Android KTX

WorkManager

  • バックグラウンド処理を行うためのライブラリ
  • 画像のアップロードのような、アプリを終了しても継続したい処理につかう
  • アプリを終了した場合に中断したい処理にはThreadPoolもしくはRxJavaを使う
  • 内部的にはJobScheduler or FirebaseJobDispatcher or AlarmManagerが使われる

WorkManagerの実行例

class UploadPhotoWorker : Worker() {
  override fun doWork() {
    // do in background
    uploadPhoto()
    return WorkerResult.SUCCESS
  }
}

val request = OneTimeWorkerRequestBuilder<UploadPhotoWorker>().build()
WorkManager.getInstance().enqueue(request)

Navigation

  • 単一Activity内のFragmentの画面遷移(Fragment Transaction)を行うフレームワーク
  • 変数(bundle)の受け渡しUp/Backの制御やDeep Linksも定義することができる
  • Android Studio 3.2 から導入されたNavigation Editorを使い編集する
    • Navigation Editor の編集結果はXMLで保存される
  • コードからはNavigationControlleなどを用いて遷移を呼び出す
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      findViewById<Button>(R.id.next_button).setOnClickListener(
        Navigation.createNavigationOnClickListener(R.id.next)
      )

      findViewById<Button>(R.id.settings_button).setOnClickListener { v ->
        val navController = v.findNavController()
        navController.navigate(R.id.settings)
      }
    }

Paging

  • Archtecture ComponentsのPagingのStableバージョンがでた
  • Recycler Viewに表示しているデータの「続きを読み込む」ライブラリ
  • 読み込み先はネットワーク、データベース、またはその両方

Slices

  • Google検索(今後はGoogleアシスタントなどにも)にアプリのコンテンツを表示するためのUIテンプレート
  • Android 4.4 (API 19) 以降で使える
  • 自身のアプリのエンゲージメントを高めるのにつかう

Andoird KTX

  • AndoirdのAPIをKotlinから扱いやすくするためのKotlinの拡張関数群
  • 既にバージョン0.3.0が公開されていたが、1.0.0-alphaが公開された
  • Android Framework以外のライブラリに対応した拡張関数が多く追加されている

Android KTXの例

// Before
adapterView.setPaging(10, avatarView.paddingTop, 10, adapterView.paddingBottom)

// After (名前付き引数を使い実現している)
adapterView.updatePadding(left = 10, right = 10)

BTW: Kotlinについて

  • Kotlinを使ったPlaySotre上のアプリは昨年比で6倍
  • ディベロッパーの35%がKotlinを使っており、毎月増加している
  • Android DevelopersのドキュメントもKotlin対応版が提供されるようになった

googlesamples/android-sunflower

Android Jetpackをふんだんに使ったサンプル(ベストプラクティス?)

AndroidX

サポートライブラリの名前が分かりづらかった問題(v4とかv7とか...) → AndroidXという名前でリファクタリング

  • 新しいAndroidのバージョンではありません
  • Support Libraryなどのパッケージをリファクタリングしたもの
  • OSにバンドルされないAndroidの拡張ライブラリ
  • JetpackにはAndroidXに含まれるものもあるし、含まれないものもある

AndroidXのリファクタリング

  • パッケージをリファクタリング
    • com.android.support.**androidx.**
    • android.databinding.**androidx.databinding.**
    • android.arch.**androidx.arch.core.**
  • v4やv7などの表記が消える
  • バージョンが28.0.0から1.0.0にリセットされる
  • 管理も厳密に (メジャーバージョンが同じなら互換性あり)

AndroidXへの移行

  • Android Studioリファクタリングツールが提供される
    • 上部メニュー > Refactor > Refactor to AndroidX...
  • 使っているライブラリのJAR/AARを変換するJetifierというツールも提供される

One more thing

一番盛り上がってたやつ

MotionLayout

  • ConstraintLayout 2.2から導入されるConstraintLayoutのサブクラス
  • Viewの状態(ConstraintSet)間をアニメーションを使って動かすことができる
  • What's new with ConstraintLayout and Android Studio design tools
  • Motion Editorでアニメーションを編集することができる

Dynamic Feature @punchdrunker / 株式会社ミクシィ

Android App Bundleの目玉機能の一つである、Dynamic Feature Modulesについてのお話。

Android App Bundle概要

アプリをいままでよりスマートに配信する事が出来る機能群の総称

  • Multi APKの代替 (何らかの理由で端末別にapkを用意できる仕組み)
  • APKサイズの最適化 (aab)
  • 必要になった時に追加でモジュールをインストールできる
    • Dynamic Feature Moduleとしてベータ公開中
  • アプリをインストールしていなくてもモジュール単位で利用可能になる
    • instant enableとして近日公開予定

(Android Studio 3.2 RC14以降でしか使えない)

Dynamic Feature Module?

アプリのインストール後に、必要になった時に機能やリソースを事後的にインストール(Dynamic Delivery)することができる仕組みのこと。

一部の端末やユーザ向けのものはアプリインストール後に必要になったときにインストールすることで、その機能が必要の無いユーザに対してはストレージ容量を抑えられる。

また回線速度に依存するが、モジュールのインストールには数秒以上かかると思ったほうがよい。

必要なくなったらuninstallすることもできる。

Dynamic Feature Moduleの使い道

  • 有料コンテンツやプレミアム機能
    • 有料登録時にインストールできればよい
  • ゲームのステージ情報
    • ステージが進んだ時にインストールすればよい
  • とにかく大きな画像などのリソースをたまに使う
    • 必要だとわかったらインストールすればよい
  • 利用者が少なく、ある程度分離された機能
    • 他サービスとの連携する機能など

(あくまで妄想)

どんな対応が必要か

  • play coreライブラリを使用
    • com.google.android.play.core.splitinstall.*
  • SplitInstallManagerを使って、利用可能なモジュールの確認やインストールを行う
    • 確認: SplitInstallManager.getInstalledModules()
    • 追加: SplitInstallManager.startInstall(module)
    • 削除: SplitInstallManager.defferedUnsintall(modules)
  • インストール完了したら(SplitInstallStateUpdatedListener)を使うだけ
    • パッケージ名を指定して、startActivity()やcreatePackageContext()するだけ

デモ

  1. 設定アプリでhochoアプリのストレージ使用量を確認
  2. hochoアプリを起動
  3. メニューからDynamic Feature Moduleを選択
  4. 写真一覧が表示されるまで待機
  5. 写真一覧が表示されたら再度設定アプリでhochoのストレージ使用量を確認

サンプルアプリ: インストールURL → hocho - Google Play

ハマりどころ(1)

  • Modules 'base' and 'gallery' contain entry 'res/layout/activity_main.xml' with different content.
    • 別モジュールでも同じリソース名は不可
  • オンデマンドモジュールのタイトルはリソースIDには、ベースモジュールで定義された文字列を指定する必要があります。無効なモジュールは「gallery」です
    • ベースとなるアプリ側でリソースIDを定義しておく必要がある(feature_names.xml)
  • implementationで利用するライブラリを定義すると、dynamic feature module側から使えない
    • apiでロードするようにしたら使えた

ハマりどころ(2)

  • ベータ機能なので、申請フォームに記入が必要
    • 半日くらい待つと使えるようになっていた
  • aabリリースしたらminSdkVersion21なのに、hdpi端末でベクターがぼやけた(伝聞)
    • VectorDrawableCompatのissueが上がっているので、そのうち直りそう

Work Manager @takahirom / 株式会社AbemaTV

Workmanagerとは

Android Jetpack - Architectureの中の1つ Google I/O 2018 で発表

Deferrable guaranteed execution (延期保証実行)

  • ツイートの送信
  • ログ送信
  • データ同期
  • メディアファイルのサーバへのアップロード

Deferrable guaranteed execution の何が問題か

  • タイミングが後になってもよく、動作が保証したい場合たくさんの解決方法がある
    • JobScheduler, FirebaseJobDespatcher, AlarmManager + BroadCastReceivers
  • たくさんのことを考慮しながら、たくさんのAPIの呼び出しを書く必要がある
    • APILevel?, FirebaseJobDespatcher? ...
    • これにWorkManagerが対応する

どんなタスクにWorkManagerを使うべきか?

  • システムがアプリを終了したとしても、動作保証したいタスクのために使う
  • アプリのプロセスが終了しても、安全に終了できるタスクのためには利用しない

    • それは、ThreadPoolかRxJavaかCoroutineを使うべき
  • 出典: Guide to background processing

例えばどんなタスク

  • ○: ツイート
  • ○: メディアファイルのサーバへのアップロード
  • ○: データのパースとデータベースへの保存

  • ×: パレットカラーを抽出してImageViewをアップデート (※1)

  • ×: データをパースしてViewをアップデート (※1)
  • ×: 課金のトランザクションを実行 (※2)

(※1): ThreadPool, RxJava, Kotlin Coroutine を使う
(※2): Foreground Service を使う

WorkManagerの使い方

// 必須
implementation "android.arch.work:work-runtime:1.0.0-alpha02"

// Firebase JobDispatcherを使う場合必要
implementation "android.arch.work:work-firebase:1.0.0-alpha02"

// Kotlinを使う場合、あったほうが簡潔に書ける
implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha02"

// テストを書ける
androidTestImplementation "android.arch.work:work-testing:1.0.0-alpha02"

WorkManagerの中心となるクラス

  • Work: 作業をするクラス。実際にやりたいことを継承して記述する
  • WorkRequest: Workのリクエストクラス
    • OneTimeWorkRequest: 一度きりのWorkを実行する場合のRequest
    • PeriodicWorkRequest: 繰り返しWorkを実行する場合のRequest

Workerを使ってみる

コードなので省略

WorkManagerの機能紹介

  • Workの依存グラフが作れる
    • then()でWorkを繋げられる
    • RxJavaのzipみたいなことができる
      • InputMergerを使う
    • Workに条件をつけられる
    • LiveDataで今の状態が見られる
      • Workにタグをつけて絞り込みなどができる
      • チェイン開始時にユニークな名前をつけて絞り込みや、同じ名前が流れたときの制御ができる

then()でWorkを繋げられる

  • 写真にフィルターをかけた後アップロードしたいときなどに利用する
  • 今回は World -> Hello, World -> HELLO, WORLD をやってみた
WorkManager.getInstance()
    .beginWith(OneTimeWorkRequestBuilder<AddHelloWorker>()
      .setInputData(mapOf(KEY to "World").toWorkData())
      .buiild()
    )
    // ここ
    .then(OneTimeWorkRequestBuilder<UpperCaseWorker>().build())
    .enqueue()

Workに条件をつけられる

  • ネットワークに繋がっている時のみ、充電時のみ実行などの条件がつけられる
    • setConstraintsで設定してあげる
val workRequest = OneTimeWorkRequestBuilder<AddHelloWorker>()
    .setInputData(mapOf(KEY to "World").toWorkData())
    // 充電がはじまると、実行される
    .setConstranits(Constraints.Builder().setRequiresCharging(true).build())
    .build()

Workにタグをつけて絞り込みなどができる

  • 好きにネームスペースが作れる
    • addTag()でタグ追加
    • getStateusesByTag()で絞り込み
    • 他にもタグ単位でキャンセルしたりできる
val taggedRequest = OneTimeWorkerRequestBuilder<AddHelloWorker>()
    .setInputData(mapOf(KEY to "WorkManager").toWorkData())
    // タグ追加
    .addTag("tag")
    .build()
WorkManager.getInstance().enqueue(taggedRequest)

WorkManager.getInstance()
    // getStatusesByTag()で絞り込み
    .getStatusesByTag("tag")
    .observe(this, Observer<List<WorkStatus>> { workStatuses ->
      val workStatus = workStatuses?.getOrNull(0) ?: return@observer
      when (workStatus.state) {
        State.SUCCEEDED -> {
          ...
        }
      }
    })

チェイン開始時にユニークな名前をつけて絞り込みや、制御ができる

  • beginUniqueWorkを使う
  • ExistingWorkPolicy は同じ名前でWorkを始めたときのポリシー
    • KEEP: 先に始めたWorkだけ実行
    • REPLACE: 先に初めたWorkをキャンセルして削除、新しいWorkを開始
    • APPEND: 先に始めたWorkの後に、両方行う
val workRequest = OneTimeWorkRequestBuilder<AddHelloWorker>()
    .setInputData(mapOf(KEY to "World").toWorkData())
    .build()
WorkManager.getInstance()
  .beginUniqueWork("Hello", ExistingWorkPolicy.KEEP, workRequest)
  .enqueue()

Inside WorkManager

WorkManagerの内部実装の基本

DBに情報を入れてから、①まずExecutorで実行する。その間にプロセスが止まっても良いように②JobSchedulerなどからExecutorを実行する

  • ① Executor
  • API 23+?
    • YES: JobScheduler
      • Executor
    • NO: App include Firebase JobDispatcher?
      • YES: Firebase JobDispatcher
        • Executor
      • NO: AlarmManager + Broadcast Receivers
        • Executor

WorkManagerの依存関係

  • Roomに依存している
    • DBの中をStethoで確認できる
      • androidx.work.workdbにWorkerが登録されている

WorkManagerのテスト

TestDriver()を使って同期的に動かしてテストできる

@Test
func test() {
  val testDriver = WorkManagerTestInitHelper.getTestDriver()
  val workRequest = OneTimeWorkRequestBuilder<AddHelloWorker>()
      .setInputData(mapOf(KEY to "World").toWorkData())
      .build()

  WorkManager.getInstance().enqueue()
  testDriver.setAllConstraintsMet(workRequest.id)

  val outputData = WorkManager.getInstance()
      .synchronous()
      .getStatusByIdSync(workRequest.id)
      .outputData
  
  assertThat(outputData.getString(KEY, "none"), equalTo("Hello World"))
}

WorkManagerのサンプル実装

Android Sunflower (alpha) 内にあるWorkManager

Android Sunflower (alpha) は、Android JetpackAndroid開発のベストプラクティスを示すガーデニングアプリ。

Workerの中でのDIは?

  • Redditに質問が上がっていた
    • 乞うご期待

ベストプラクティス

  • システムがアプリが終了したとしても、動作保証したタスクのために使う
  • DataクラスはデータストアではないのでちゃんとRoomなどを使う
  • 実行できる機会をうかがうようにConstraintで条件を書こう

    例えば画像の書こうとアップロードをするときに、アップロードのWorkにネットワークが必要だとしておけば、飛行機に乗っている間に画像の加工までは終わって、繋がったらアップロードしてくれるなどができる

まとめ

  • システムがアプリが終了したとしても、動作保証したいタスクのために使う
  • OSの依存、使うべき仕組みの切り替えなど、とても楽になる。ボイラープレートがなくなる
  • Workに制約をつけれる、繋げられる、タグがつけられる、名前がつけられる、状態をLiveDataで取得できる
  • 使いみちは多いので、使ってみると良さそう