แจ้งให้ user รู้ว่าแอปมี version ใหม่ด้วย in-app updates
เคยไหม ที่โดน user บ่นใส่ทีมคอมมูนิตี้ซึ่งมีหน้าที่ดูแล user ในแอพที่เรา develop อยู่ ว่าแอพอัพเดตแล้วทำไมไม่บอก มาลวงมาหลอกฉันเล่นทำไม เดี๋ยวๆ
คือแอพที่เราดูแลอยู่คือแอพ Anna แล้ว user ชอบบ่นว่าทำไมพี่เลี่ยน อัพแอพ version ใหม่เลยไม่บอกเขาเลย เราเลยคิดว่าการใช้ in-app updates เป็นการแก้ปัญหาโดยที่เราช่วยฝั่งคอมมูนิตี้อีกแรงนึง
แอพ Anna จะมีตัวละครในแอพและในเพจ 3 ตัวด้วยกันคือ
พี่เลี่ยน เป็นเอเลี่ยนชื่อแอนนาวัน
ณ๊องค์ เป็นเอเลี่ยนเหมือนแอนนาวัน ชื่อแอนนาทู
และแป๊ะก๊วยคืออาเจ๊กคนหนึ่ง
ซึ่งเพจ Anna คนที่มาสื่อสารกับลูกเพจหรือ user ก็คือเจ้าแอนนาวัน หรือพี่เลี่ยนนั่นเอง
ก่อนหน้านี้ทีมเราจะใช้ยิงจากหลังบ้าน เพื่อบอกว่า มีแอพ version ใหม่บน store มาแล้วนะ ซึ่งจะมีเคสแบบต้องอัพเดต ไม่งั้นแอพพัง เอ้ยยย มีการปรับเปลี่ยนบางอย่างทีจำเป็นกับ user กับไม่ต้องอัพเดตก็ได้ แล้วขึ้น dialog ออกมา หน้าบ้านแบบเราต้องไปบอกทีมหลังบ้าน ตอนที่เรา check เองแล้วว่าแอพ version ใหม่นั้น ขึ้น store เรียบร้อยแล้วนะ ไหนจะต้องบอกคอมมูเพื่อให้เขาบอก user ว่าพี่เลี่ยนมีอัพเดตแล้วนะ
เราเห็นแอพอะดวงทำ เอ้ออออ มันดีย์อ่ะ คือ user เปิดแอพมารู้เลย ว่ามีอัพเดตจาก store แล้วนะ
แล้วสิ่งนี้ที่ช่วยเราแก้ปัญหานี้คืออะไรหล่ะ?
จากบล็อกนี้ของพี่ยู เราจึงได้รู้ว่าเจ้าสิ่งนี้เรียกว่า in-app updates นั่นเองงงง
ส่วนอันนี้ document
Support in-app updates | Android Developers
Keeping your app up-to-date on your users' devices enables them to try new features, as well as benefit
ปล. ตอนที่กำลังเขียนบล็อกนี้ยังไม่ได้อยู่บน production นะเออ แค่ทดลองก่อนจ้า (ตอนที่ได้อ่านบล็อกน่าจะขึ้น production จริงไปแล้วก็เป็นได้ 555) แต่ app bundle คือของจริง
สรุปสั้นๆจาก document นะ เกี่ยวกับเจ้า in-app updates สิ่งที่ต้องเตรียมมีดังนี้
- device ใช้ได้ตั้งแต่ Android 5.0 (API level 21) ขึ้นไป
- และใช้ Play Core library 1.5.0 หรือสูงกว่า
หน้าตามันมี 2 แบบ คือ Flexible กับ Immediate
แบบ Flexible มันจะขึ้นเป็น dialog ออกมา ซึ่งเราจะอัพก็ได้ ไม่อัพก็ได้ และมันจะแอบ download แอพ version ใหม่ใน background
ส่วนแบบ Immediate มันจะขึ้นเต็มจอ และบังคับอัพเดตกลายๆ (เพราะกดปุ่มปิดได้ งงม่ะ) ดังนั้นเราจะไม่สามารถใช้แอพได้เมื่อแอพกำลัง download version ใหม่อยู่
ก่อนที่จะทำเจ้า in-app updates ได้นั้น แอพของเราจะต้องเป็น Android App Bundles เท่านั้น มันไม่ support apk เน้ออออออ ดู document สิ
ถ้ายังไม่ได้ทำ Android App Bundles อ่านก่อนที่นี่เลยจ้า
มาเริ่มเขียนโค้ดกันเถอะ~~
ก่อนอื่น ไปเพิ่มที่ dependency ใน module ของเราก่อนนะ
dependencies {
implementation 'com.google.android.play:core:1.6.1'
}
Play Core Library release notes | Android Developers
When you publish your app with an Android App Bundle, Google Play's Dynamic Delivery allows your app to download…developer.android.com
เมื่อ sync gradle เรียบร้อยแล้ว เรามา init AppUpdateManagerFactory
val appUpdateManager = AppUpdateManagerFactory.create(this)
เราเลือกค่า appUpdateInfo เพื่อนำไป check ว่า version ใหม่มายังอ่ะเลี่ยน (ซึ่งเลี่ยนบอกไปถาม Play Store มาอีกที)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
จากนั้นเพิ่ม listener ไว้เพื่อ check ว่า ถ้า status เป็น update แล้วน้าน ให้ทำสิ่งใดต่อไป ซึ่งตัว isUpdateTypeAllowed
นั้น เราสามารถ check ได้ว่า เป็นการ update แบบ FLEXIBLE
หรือ IMMEDIATE
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
// TODO: Request the update.
}
}
ในเมื่อมันเลือกได้ว่าอัพเดตแบบไหน เราต้องไปสั่งตรงไหนนะ
ใน TODO
นั้น เราใส่เพิ่มว่า ถ้าเป็น condition นี้น้านนน จะให้ทำอัลไลต่อ เช่น เมื่อ
แอพมีอัพเดต และถ้าบังคับอัพเดตเป็น IMMEDIATE
ซึ่งตรง parameter ที่สองนั้น ใส่ให้ตรงกับ condition เราด้วยเน้อ
สรุปโค้ดจะเป็นแบบนี้จ้า
เราทดลองกับ Internal Test แอพของเราเอง ว่าใส่แล้วหน้าตาเป็นอย่างไร ซึ่งความงงก็คือ
- ตอนทดสอบ เราลด versionCode ให้น้อยกว่าบน Internal Test ไม่งั้นมันไม่เข้า condition หน้าต่างของหัวใจมันจะไม่มาจ้า ส่วน versionName ไม่มีผลจ้า
- อันนี้ที่เราลองใส่ทั้ง
FLEXIBLE
หรือIMMEDIATE
แล้ว มันก็เป็น true ทั้งคู่เว้ย อ่ะโผล่มาทั้งสองแบบตาม condition งงไหม งง และแน่นอนว่า เราต้องเลือกเพียงอย่างเดียวหว่ะ ว่าจะเอาแบบไหนแน่ เพราะใส่ condition ทั้งสองแบบแล้วมันไม่ขึ้นเลย
ซึ่งจริงๆ ถือว่าเราทำหน้าที่ได้อย่างเรียบร้อย ในการให้หน้าต่างมันขึ้นมา แต่ๆๆๆๆ ยังไม่จบจ้า อย่าลืมว่า กด update แล้ว ยังไงต่อนะ
Update App แบบ Flexible
ในที่นี้จะอธิบายแบบ FLEXIBLE ต่อแล้วกันเนอะ ไม่ว่าจะเป็น เคส user ซื่อๆกด update หรือ เคส user ขี้เกียจ ยังก่อนเดี๋ยวมาอัพ เราต้องมา handle ต่อที่ onActivityResult จ้า ดังนี้
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == MY_REQUEST_CODE) {
when (resultCode) {
Activity.RESULT_OK -> {
popupSnackbarForState("App is uploading", Snackbar.LENGTH_INDEFINITE)
}
Activity.RESULT_CANCELED -> {
popupSnackbarForState("You cancel for update new version.", Snackbar.LENGTH_SHORT)
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {
popupSnackbarForState("App download failed.", Snackbar.LENGTH_SHORT)
}
}
}
}
Activity.RESULT_OK
สำหรับเคสของ user คนซื่อ เราจะให้ขึ้น snackbar บอกว่าแอพกำลังอัพเดตอยู่Activity.RESULT_CANCELED
สำหรับเคสของ user คนขี้เกียจ เราจะขึ้นบอกเพียงสั้นๆว่า แกไม่อัพแอพชั้นเนอะ 55ActivityResult.RESULT_IN_APP_UPDATE_FAILED
สำหรับเคสของ user คนซื่อ กด download app แล้ว ดั๊นนนนนนนน อัพเดตไม่ได้ ก็ส่งไปบอกเขาสั้นๆซะหน่อย
ถ้า user คนซื่อ download แอพเสร็จแล้วทำไรต่อง่ะ
ก่อนอื่นเราต้องไปสร้าง listener ให้กับเจ้า appUpdateManager กันก่อน ซึ่งเราก็ควรขยับการประกาศเจ้า appUpdateManager
ไปเป็น global ซะ เพราะจะเอาไปใช้ต่อ
appUpdateManager.registerListener(this)
เมื่อ register เสร็จแล้วพบว่าตรง this ยัง error อยู่ ดังนั้นเราจึงต้อง implement InstallStateUpdatedListener เพิ่มจ้า
หลังจากเพิ่มปุ๊ปก็ override function ปั๊ป หน้าตาเป็นแบบนี้
override fun onStateUpdate(state: InstallState) {
if (state.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
}
การทำงาน ถ้า install status คือ download แอพอัพเดตเสร็จแล้ว จะให้ทำอะไรต่อ ในที่นี้คือขึ้น snackbar ที่มี action เพื่อบอก user ว่าแอพอัพเสร็จแล้วนะ จะ restart app ไหม
private fun popupSnackbarForCompleteUpdate() {
Snackbar.make(
findViewById(R.id.rootview),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("RESTART") {
appUpdateManager.completeUpdate()
appUpdateManager.unregisterListener(this@MainActivity)
}
show()
}
}
ถ้า user จะ restart app หลังจากกด action ที่ snackbar แล้วนั้น เพื่อกด restart แอพมันจะจัดการ install แอพใหม่จากบน Play Store ให้เลย และ unregister ที่เราใส่ไว้เมื่อกี้ จบปิ้ง
สรุปโค้ดรวมในรอบนี้
Update App แบบ Immediate
เราสามารถ handle คำสั่งนี้ appUpdateManager.completeUpdate()
ได้ทั้งการทำงาน Background คือแอบทำอยู่เงียบๆ แบบโค้ดทางด้านบน และ Foreground คือโชว์ออกมาให้เราเห็นโดยใส่เข้าไปใน onResume()
ดังนี้
override fun onResume() {
super.onResume()
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
try {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
MY_REQUEST_CODE)
}
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
มันจะขึ้นหน้าตา download app ให้เราดูเลย หลังจากนั้น install เหมือนแบบ Flexible จากนั้น restart แอพใหม่ เป็นอันจบพิธี
สรุปโค้ดรอบสุดท้าย
เมื่อใส่โค้ดทั้งสองอย่างถูกต้องแล้ว จะประสบปัญหา หลังจาก restart กลับมาใหม่ พบว่าแอพเปิดซ้อนกัน และขึ้นให้ฉัน update อยู่หว่ะ ทำไงต่อไปดี มันบัคๆอยู่เพราะทดสอบกับ Internal Test หรอ ;__;
ข้อควรระวังในการทดสอบ
- ปรับ version code ให้ตํ่ากว่าตัวที่อยู่ใน store (ส่วน version name แล้วแต่ดุลยพินิจและจักรยาน เอ้ยยย วิจารณญาณของท่าน)
- ตอนที่เรา build บนเครื่องเพื่อ test นั้น ต้องปรับ Build Variants ให้ตรงกับที่เรา upload app ลง play store นะ ไม่งั้นตอน download และ install เสร็จมันเด้งหน้า Activity ซ้อน และเด้งหน้าตา update ให้เราดูต่างหน้าอีก อยู่ในวังวนแห่งการ update app อยู่รํ่าไป
การนำไปปรับใช้
เราไปเอาปรับใช้คล้ายๆทีมจอย คือ ของเดิมคือถ้า user จะ download แอพแล้ว ก็จะพาไปหน้า Play Store เพื่อกด update app แต่เปลี่ยนเป็น update in-app update เป็นแบบ Immediate แทน
ก่อนจบบล็อกสรุปกันนิดๆหน่อยๆ
ทริคเล็กน้อย ไม่ว่าจะเป็นแบบ IMMEDIATE หรือ FLEXIBLE มันจะตรวจเองเลยว่า device นั้นต่อ WiFi หรือยัง ถ้ายังจะมีถามด้วยนะว่าจะต่อ WiFi หรือจะโหลดเลย
ทั้งหมดทั้งมวลนั้น ขอขอบคุณพี่ยู u naja ในการช่วย solve ปัญหาการ update install แล้วแอพเด้งขึ้นมาให้ update อีกแล้วด้วยมา ณ ที่นี้ค่าาา ~~ และขอบคุณชาวแว่นดำที่ตรวจให้บล็อกให้เราด้วยจ้า นานหน่อยแต่คนอ่านน่าจะได้สาระกลับไปแน่นอน
Reference:
สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ