Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Android In-app updates [ja]

Introduction to Android In-app updates [ja]

Introduction to Android In-app updates [ja]

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

  1. In-app updates で出来ること Type = FLEXIBLE DL 中に UI の操作が可能

    好きなタイミングでインス トール・再起動 Wi-Fi かどうかを考慮
  2. In-app updates で出来ること Type = IMMEDIATE DL から インストール・再起 動まで

    UI 操作を防ぐことが 出来る Wi-Fi かどうかを考慮
  3. First val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } 更新リクエストをする前に、ストアにアプリの最新バージョンがあるか確認します
  4. AppUpdateManager val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } 更新を確認するには In-app updates のマネージャクラスのインスタンスを生成します
  5. Task<AppUpdateInfo>#addOnSuccessListener val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } AppUpdateManager から取得した Task<AppUpdateInfo> にリスナーを登録 AppUpdateInfo には更新するべきかを判断出来る情報が格納されています
  6. appUpdateInfo.updateAvailability() val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } AppUpdateInfo#updateAvailability() でストアにアップデートがあるか確認します
  7. InstallStatus.class public @interface UpdateAvailability { int UNKNOWN = 0; int

    UPDATE_NOT_AVAILABLE = 1; int UPDATE_AVAILABLE = 2; int DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS = 3; } UpdateAvailability はこれらが定義されており DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS は少し特殊なので後述します
  8. appUpdateInfo.isUpdateTypeAllowed(type) val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } AppUpdateInfo#isUpdateTypeAllowed で指定タイプが利用できるか確認します FLEXIBLE or IMMEDIATE
  9. startUpdateFlowForResult(…) val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } startUpdateFlowForResult で更新訴求を表示し、更新フローを開始します
  10. startUpdateFlowForResult(…) appUpdateManager.startUpdateFlowForResult( // addOnSuccessListener で得られる AppUpdateInfo を指定 appUpdateInfo, // FLEXIBLE

    または IMMEDIATE AppUpdateType.IMMEDIATE, // アップデートのリクエストをする Activity this, // onActivityResult() で使うための Request Code を指定でき、キャンセルなどの判断をする REQUEST_CODE_UPDATE ) startUpdateFlowForResult で更新方法の指定や onActivityResult で使うためのリクエストコードを指定します
  11. First fin. val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info ->

    if (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } }
  12. startUpdateFlowForResult(…) val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) } } startUpdateFlowForResult で更新訴求を表示し、更新フローを開始します
  13. Second val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())

    { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) AppUpdateManager に更新フローの状態を監視するためのリスナーを登録します FLEXIBLE タイプで更新をする場合には必要になります
  14. InstallStateUpdatedListener val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())

    { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) 後々に、unregisterListener でリスナー解除をするために、この例では変数にします
  15. InstallStatus val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())

    { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) ダウンロード完了(DOWNLOADED)した場合には、Snackbar などの UI で更新を促します
  16. InstallStatus.class public @interface InstallStatus { int UNKNOWN = 0; int

    REQUIRES_UI_INTENT = 10; int PENDING = 1; int DOWNLOADING = 2; int DOWNLOADED = 11; int INSTALLING = 3; int INSTALLED = 4; int FAILED = 5; int CANCELED = 6; } 最低限 DOWNLOADED の判断は必要になります
  17. registerListener && unregisterListener val listener = InstallStateUpdatedListener { installState ->

    when (installState.installStatus()) { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // manager.registerListener(listener) // manager.unregisterListener(listener) 次に、どのタイミングでリスナーの登録・解除をするか説明していきます 次に、どのタイミングでリスナーの登録・解除をするか説明していきます
  18. registerListener val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) manager.registerListener(listener) } } リスナーの登録は更新フロー開始のところで登録します 解除は後述
  19. Snackbar val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())

    { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) 先程説明した InstallStatus で判断し、Snackbar で更新を促します
  20. Snackbar private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {

    setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() } この例では Snackbar のアクションで新しいバージョンのインストールをします
  21. Snackbar#setAction private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {

    setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() }
  22. appUpdateManager.completeUpdate() private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {

    setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() } AppUpdateManager#completeUpdate() でインストールを実行します completeUpdate をフォアグラウンドで呼び出した場合は、アプリが再起動され completeUpdate をバックグラウンドで呼び出した場合は、サイレントインストールされます
  23. appUpdateManager.unregisterListener(listener) private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {

    setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() } ここで、リスナーの解除をします
  24. Third fin. private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply

    { setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() }
  25. DOWNLOADED val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE)) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) manager.registerListener(listener) } } 最初に説明した更新があるかどうかだけでは少し不十分のため説明をしていきます
  26. DOWNLOADED val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.installStatus() == DOWNLOADED) { popupSnackbarForCompleteUpdate() } else if (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(FLEXIBLE) ) { manager.startUpdateFlowForResult(info, FLEXIBLE, this, REQUEST_CODE_UPDATE) manager.registerListener(listener) } } DOWNLOADED 状態の場合、ユーザに更新を促す必要があります 更新が完了するまで、そのデータはデバイスストレージに保存されたままとなります
  27. startUpdateFlowForResult(…) val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(IMMEDIATE)) { manager.startUpdateFlowForResult(info, IMMEDIATE, this, REQUEST_CODE_UPDATE) } } startUpdateFlowForResult で更新訴求を表示し、更新フローを開始します
  28. DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS 最初に説明した更新があるかどうかだけでは少し不十分のため説明をしていきます val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info ->

    if (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(IMMEDIATE)) { manager.startUpdateFlowForResult(info, IMMEDIATE, this, REQUEST_CODE_UPDATE) } }
  29. DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS val manager = AppUpdateManagerFactory.create(this) manager.appUpdateInfo.addOnSuccessListener { info -> if

    (info.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS || (info.updateAvailability() == UPDATE_AVAILABLE && info.isUpdateTypeAllowed(IMMEDIATE)) ) { manager.startUpdateFlowForResult(info, IMMEDIATE, this, REQUEST_CODE_UPDATE) } } DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS は更新が途中になっている状態であり その場合は startUpdateFlowForResult で更新フローを再開します