Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Introduction to Android In-app updates [ja]
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Daichi Furiya (Wasabeef)
May 17, 2019
Programming
770
5
Share
Introduction to Android In-app updates [ja]
Introduction to Android In-app updates [ja]
Daichi Furiya (Wasabeef)
May 17, 2019
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
6
2.7k
About Flutter Architecture
wasabeef
1
310
2023 Flutter/Dart Summary
wasabeef
0
130
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
230
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3.1k
What it Takes to be a Flutter Developer
wasabeef
0
240
FlutterKaigi 2022 Keynote
wasabeef
1
710
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
wasabeef
2
1.5k
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.2k
Other Decks in Programming
See All in Programming
ローカルLLMでどこまでコードが書けるか / How much code can be written on a local LLM
kishida
2
370
関係性から理解する"同一性"の型用語たち
pvcresin
1
240
Surviving Black Friday: 329 billion requests with Falcon!
ioquatix
0
3.2k
20年以上続くプロダクトでも使い続けられる静的解析ツールを求めて
matsuo_atsushi
0
150
実践ハーネスエンジニアリング:ステアリングループを実例から読み解く / Practical Harness Engineering: Understanding Steering Loops Through Real-World Examples
nrslib
5
5.6k
Agentic UI in the Frontend: Architectures with Open Standards @JAX 2026 in Mainz
manfredsteyer
PRO
0
120
Spec-Driven Development with AI Agents (Workshop, May 2026)
antonarhipov
3
400
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
420
[BalkanRuby 2026] Drop your app/services!
palkan
3
570
AlarmKitで明後日起きれるアラームアプリを作る
trickart
0
140
20260514 - build with ai 2026 - build LINE Bot with Gemini CLI
line_developers_tw
PRO
0
450
なぜあなたのコードには「コシ」がないのか?〜AI時代に問う、最後まで美味しい設計と戦略〜 #phpconkagawa / phpconkagawa2026
shogogg
0
210
Featured
See All Featured
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
The Pragmatic Product Professional
lauravandoore
37
7.3k
GraphQLとの向き合い方2022年版
quramy
50
15k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
330
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
310
SEO for Brand Visibility & Recognition
aleyda
0
4.5k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.4k
Prompt Engineering for Job Search
mfonobong
0
310
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
510
Writing Fast Ruby
sferik
630
63k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
250
From π to Pie charts
rasagy
0
180
Transcript
In-app updates Wasabeef Shibuya.apk #34
About me Daichi Furiya Google Developers Expert CATS, CyberAgent @wasabeef_jp
wasabeef
In-app updates
Ref: Developer Keynote
In-app updates で出来ること
In-app updates で出来ること Type = FLEXIBLE DL 中に UI の操作が可能
好きなタイミングでインス トール・再起動 Wi-Fi かどうかを考慮
In-app updates で出来ること Type = IMMEDIATE DL から インストール・再起 動まで
UI 操作を防ぐことが 出来る Wi-Fi かどうかを考慮
流れを知る
FLEXIBLE
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
FLEXIBLE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
IMMEDIATE
IMMEDIATE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
IMMEDIATE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
IMMEDIATE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
IMMEDIATE の流れを知ろう https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/
Code
Android 5.0+ (API 21+) Play Core Library 1.5.0+ Require
Gradle dependencies { implementation 'com.google.android.play:core:1.5.0' }
まず訴求を出すには?
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) } } 更新リクエストをする前に、ストアにアプリの最新バージョンがあるか確認します
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 のマネージャクラスのインスタンスを生成します
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 には更新するべきかを判断出来る情報が格納されています
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() でストアにアップデートがあるか確認します
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 は少し特殊なので後述します
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
FLEXIBLE IMMEDIATE
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 で更新訴求を表示し、更新フローを開始します
startUpdateFlowForResult(…) appUpdateManager.startUpdateFlowForResult( // addOnSuccessListener で得られる AppUpdateInfo を指定 appUpdateInfo, // FLEXIBLE
または IMMEDIATE AppUpdateType.IMMEDIATE, // アップデートのリクエストをする Activity this, // onActivityResult() で使うための Request Code を指定でき、キャンセルなどの判断をする REQUEST_CODE_UPDATE ) startUpdateFlowForResult で更新方法の指定や onActivityResult で使うためのリクエストコードを指定します
FLEXIBLE IMMEDIATE キャンセル可能
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) } }
FLEXIBLE 実装方法・注意点
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 で更新訴求を表示し、更新フローを開始します
FLEXIBLE 更新フローの 状態を知るには?
Second val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())
{ DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) AppUpdateManager に更新フローの状態を監視するためのリスナーを登録します FLEXIBLE タイプで更新をする場合には必要になります
InstallStateUpdatedListener val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())
{ DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) 後々に、unregisterListener でリスナー解除をするために、この例では変数にします
InstallStatus val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())
{ DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) ダウンロード完了(DOWNLOADED)した場合には、Snackbar などの UI で更新を促します
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 の判断は必要になります
registerListener && unregisterListener val listener = InstallStateUpdatedListener { installState ->
when (installState.installStatus()) { DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // manager.registerListener(listener) // manager.unregisterListener(listener) 次に、どのタイミングでリスナーの登録・解除をするか説明していきます 次に、どのタイミングでリスナーの登録・解除をするか説明していきます
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) } } リスナーの登録は更新フロー開始のところで登録します 解除は後述
Snackbar を表示して 更新を促す
Snackbar val listener = InstallStateUpdatedListener { installState -> when (installState.installStatus())
{ DOWNLOADING -> println("downloading... ") DOWNLOADED -> popupSnackbarForCompleteUpdate() } } // appUpdateManager.registerListener(listener) // appUpdateManager.unregisterListener(listener) 先程説明した InstallStatus で判断し、Snackbar で更新を促します
Snackbar private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {
setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() } この例では Snackbar のアクションで新しいバージョンのインストールをします
Snackbar#setAction private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {
setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() }
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 をバックグラウンドで呼び出した場合は、サイレントインストールされます
appUpdateManager.unregisterListener(listener) private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply {
setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() } ここで、リスナーの解除をします
Third fin. private fun popupSnackbarForCompleteUpdate() { Snackbar.make(findViewById(R.id.main), "DL 完了しました。”,LENGTH_INDEFINITE) .apply
{ setAction("更新") { appUpdateManager.completeUpdate() appUpdateManager.unregisterListener(listener) } }.show() }
更新せずに アプリを終了したら?
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) } } 最初に説明した更新があるかどうかだけでは少し不十分のため説明をしていきます
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 状態の場合、ユーザに更新を促す必要があります 更新が完了するまで、そのデータはデバイスストレージに保存されたままとなります
IMMEDIATE 実装方法・注意点
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 で更新訴求を表示し、更新フローを開始します
更新が完了せずに アプリを終了したら?
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) } }
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 で更新フローを再開します
Conclusion..
簡単 ストアを開かずにアップデート出来る 回線状況も考慮されている ただし、強制アップデートではない ※Firebase などサーバで制御は必要 Conclusion
References: - https://d.android.com/guide/app-bundle/in-app-updates - https://joebirch.co/2019/05/07/exploring-in-app-updates-on-android/ Code Resources
Image Resources Photos: - https://unsplash.com - https://www.pexels.com Illustrations: - http://www.chojugiga.com
- https://www.irasutoya.com
twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef