정상에서 IT를 외치다

[in-app updates] 안드로이드 인 앱 업데이트 사용기 본문

안드로이드

[in-app updates] 안드로이드 인 앱 업데이트 사용기

Black-Jin 2019. 6. 21. 11:24
반응형

안녕하세요. 블랙진입니다.


2019 구글 IO에서 발표한 내용중 인 앱 업데이트에 대한 사용기를 소개해 볼려고 합니다.


In-App Update


새로운 버전의 앱을 구글 스토어에 등록했을 때 알림을 통해 사용자에게 업데이트를 권하는 방법은 개발자 마다 한번은 고민했을 겁니다. 이에 대해 2019 구글 IO에서는 그 뱡향을 제시해 줬는데요. 다음은 2019 구글 블로그의 내용 중 일부입니다.


today we’re also moving in-app updates from beta to stable. The ability to dynamically update apps is something you’ve been requesting for a long time. Let’s say you have a crucial bug in your app, and you need to push it out right away; you don’t want to wait until users discover an update in the Play Store. Now you can.


내용을 보시면 beta 버전에서 stable 버전을 이번 2019년에 배포 했다고 하는데 어디 한번 써볼까요?



Flexible vs Immediate


Flexible(권장 업데이트)



출처


유연한(권장) 업데이트입니다. 앱을 실행시키면 업데이트 여부를 파악해 다이얼로그를 사용자에게 보여줍니다. 업데이트를 클릭하게 되면 백그라운드에서 업데이트가 진행되며 업데이트가 끝나면 사용자에게 완료 메시지를 보여줍니다. 여기서 사용자가 앱을 다시 시작할지 말지를 선택하게 되는데요. 이렇게 플레이 스토어로 이동하지 않고 업데이트 여부를 사용자가 선택할 수 있게하여 앱에 대한 사용자 경험을 계속 유지시켜 줄 수 있습니다.




Immediate(필수 업데이트)



출처


즉시(필수) 업데이트입니다. 앱을 실행시키면 업데이트를 권하는 전체화면이 나옵니다. 물론 이 화면에서 뒤로가기를 통해 이전화면으로 넘어 갈 수 있지만 flexible 업데이트 보다는 사용자에게 업데이트를 더 요구하는 방법입니다. 역시 플레이 스토어로 이동하지 않고 해당 화면에서 진행되며 업데이트가 완료 되면 앱이 재실행됩니다. 





사용방법


1.  Play Core Library 설치


인 앱 업데이트는 안드로이드 5.0(Api level 21) 이상부터 사용 가능합니다. 그리고 Play Core Library 1.5.0 이상을 안드로이드 스튜디오에 추가해 주셔야 됩니다.


//play store core
implementation 'com.google.android.play:core:1.5.0'


2. AppUpdateManager 초기화

appUpdateManager = AppUpdateManagerFactory.create(this)

appUpdateManager?.let {
it.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->

if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// or AppUpdateType.FLEXIBLE
appUpdateManager?.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE, // or AppUpdateType.FLEXIBLE
this,
REQUEST_CODE_UPDATE
)
}
}
}


AppUpdateManagerFactory를 통해 초기화를 해줍니다. 그리고 앱이 업데이트 가능한 상태인지?(UpdateAvailability.UPDATE_AVAILABLE), 해당 업데이트 타입을 적용할 수 있는지?(isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) 체크 후 업데이트를 진행합니다.



3. 업데이트 취소


AppUpdateManager 의 startUpdateFlowForResult 는 startActivityForResult 와 같은 동작을 합니다. 따라서 onActivityResult 를 통해 취소 결과를 반환해 줍니다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (requestCode == REQUEST_CODE_UPDATE) {
if (resultCode != Activity.RESULT_OK) {
toast("업데이트가 취소 되었습니다.")
}
}
}



4. Flexible 업데이트


1) 리스너를 추가해 줍니다.(생명주기에 맞춰 해제 코드도 추가해 주세요)

listener = InstallStateUpdatedListener {
// Handle install state
if (it.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
}

appUpdateManager?.registerListener(listener)


리스너를 통해 백그라운드에서 앱 설치가 완료되면 재시작 요청을 사용자에게 보여줍니다.


2) 설치/재시작 요청 보여주기

private fun popupSnackbarForCompleteUpdate() {

val snackbar = Snackbar.make(findViewById(R.id.clActivityMain), "업데이트 버전 다운로드 완료", 5000)
.setAction("설치/재시작") {
appUpdateManager?.completeUpdate()
}

snackbar.show()
}


사용자가 직접 completeUpdate() 함수를 실행 시켜 주어야만 업데이트가 적용됩니다. 그전에는 앱을 아무리 다시 시작해도 적용이 되지 않습니다. 이 때 AppUpdateManager 상태를 봐야 되는데 그전에 AppUpdateManager에는 어떤 상태가 있는지 확인해 보겠습니다.


appUpdateManager.appUpdateInfo

AppUpdateManager 에서 getAppUpdateInfo 를 통해 두 가지 상태를 알아올 수 있습니다.



UpdateAvailablity(업데이트 가능 여부)

public @interface UpdateAvailability {
int UNKNOWN = 0;
int UPDATE_NOT_AVAILABLE = 1;
int UPDATE_AVAILABLE = 2;
int DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS = 3;
}


InstallStatus(설치가 얼마나 진행 되었는지)

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;
}


그럼 다시 본론으로 들어가서 2) 설치/재시작 요청 보여주기 단계에서는 어떤 상태 일까요?


UpdateAvailability : 3

InstallStatus : 11


업데이트가 이미 진행된 상태에서는 UpdateAvailablity 는 DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS 이며 InstallStatus 는 DOWNLOADED 상태가 표시됩니다. (만약 백그라운드에서 아직 설치가 진행 중인 상태에서는 UpdateAvailablity : 3 , InstallStatus : 2 이 나옵니다. 설치/재시작을 누르면  UpdateAvailablity : 3 , InstallStatus : 3 가 되며 설치/재시작이 모두 완료 되면 UpdateAvailablity : 1 , InstallStatus : 0  상태가 됩니다.) 


// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
@Override
protected void onResume() {
 
super.onResume();

  appUpdateManager
     
.getAppUpdateInfo()
     
.addOnSuccessListener(appUpdateInfo -> {
             
...
             
// If the update is downloaded but not installed,
             
// notify the user to complete the update.
             
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                  popupSnackbarForCompleteUpdate
();
             
}
         
});
}


위 코드는 구글 개발 페이지에서 가져온 코드 입니다. onResume 에서 상태 처리를 하지만 위와 같이 한 경우 모든 app entry point 에서 확인을 해줘야 된다고 하죠? 필자는 적절한 방법이라 생각되지 않아 다르게 처리했습니다. 이 부분에 관해서는 개발자 분들의 판단에 맞춰 InstallStatus.DOWNLOADED 를 어디서 주기적으로 확인하면 좋을지 결정해서 작업하시면 되겠습니다.



5. Immediate 업데이트



즉시 업데이트에서 버튼을 클릭하게 되면 오른쪽 다운로드 화면으로 넘어가게 됩니다. 근데 저 화면에서 뒤로가기가 적용됩니다.... 다운로드 중에 뒤로가기 혹은 백그라운드로 이동하게 되면 이에 맞춰 추가 코드를 작성해 주어야합니다.  왜냐하면 위 화면에서 벗어나게 되면 백그라운드에서 업데이트가 완료 되어도 앱에 적용되지 않습니다. AppUpdateManager의 상태를 확인해 startUpdateFlowForeResult를 재동작 시켜주어야 합니다. 그러면 오른쪽 화면의 상황에서는 AppUpdateManager 는 무슨 상태일까요?


UpdateAvailablity(업데이트 가능 여부)

public @interface UpdateAvailability {
int UNKNOWN = 0;
int UPDATE_NOT_AVAILABLE = 1;
int UPDATE_AVAILABLE = 2;
int DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS = 3;
}


InstallStatus(설치가 얼마나 진행 되었는지)

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;
}


UpdateAvailability : 3

InstallStatus : 2


업데이트가 이미 진행된 상태에서는 UpdateAvailablity 는 DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS 이며 설치 상태는 두가지로 나뉩니다. 설치 중이면  InstallStatus : 2 설치가 완료된 상태면 InstallStatus : 11 로 나옵니다. 이에 관해서 구글에서는 어떻게 처리했을까요? 역시 구글 개발 페이지 코드를 보겠습니다.


// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
@Override
protected void onResume() {
 
super.onResume();

  appUpdateManager
     
.getAppUpdateInfo()
     
.addOnSuccessListener(
          appUpdateInfo
-> {
           
...
           
if (appUpdateInfo.updateAvailability()
               
== UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
               
// If an in-app update is already running, resume the update.
                manager
.startUpdateFlowForResult(
                    appUpdateInfo
,
                    IMMEDIATE
,
                   
this,
                    MY_REQUEST_CODE
);
           
}
         
});
}


OnResume 에서 UpdateAvailablity 상태가 DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS 인 경우 startUpdateFlowForResult 를 실행 시켜 줬네요. 이 코드는 다운로드 화면에서 뒤로간 경우로 onResume이 적용되어 다시 다운로드 화면으로 강제 이동됩니다. 이 부분 역시 업데이트 상태에 따라 개발자 분들의 판단에 맞춰 예외 처리를 해주시면 됩니다. 필자는 즉시 업데이트 시에는 구글의 위 방식이 적절한 것 같습니다.




테스트 하기



필자는 구글 콘솔의 내부 테스트 트랙에 앱을 업데이트하여 테스트 했습니다. 내부 테스트 트랙은 앱을 올리면 빠르게(1분 미만 - 필자가 테스트 했을 때 기준) 스토어에 적용되므로 빠르게 인 앱 업데이트를 테스트 해 볼 수 있습니다. 


추가) FakeAppUpdateManager를 사용해서 테스트를 진행할 수 있다는 내용을 댓글(서영락님)을 통해 알게 되었습니다. 사용법은 구글문서를 확인해주세요



문제 이슈


in-app updates 문서의 맨 아래를 보면 아래와 같이 나와 있습니다.


내용을 정리하면


1. 테스트 하기전 구글 스토어에서 다운 받은 앱을 사용하세요.

2. 구글 스토어에 있는 어플리케이션 ID와 인증키가 동일해야 합니다.

3. 낮은 버전에서 높은 버전으로 테스트를 진행하세요.

4. 구글 플레이 캐시로 인해 업데이트가 안될 수 있습니다. 구글플레이 스토어를 완전히 종료 후 실행한 후 "나의 앱/게임 탭"에서 업데이트 가능 여부를 확인해 주세요.


필자는 처음에 스토어에 업데이트 되었지만 동작이 안되 코드가 잘못 된줄 알았습니다. 하지만 구글 스토어를 완전히 종료 해주고 재 실행한 후 구글 스토어 네비게이션 드로워에 있는 "내 앱/게임" 에서 "업데이트 대기 중 목록"에 나와 있는지 확인 후 실행해주면 잘 적용됩니다.



마무리


Immediate 업데이트 방식을 아이웨딩 앱에서 어떻게 동작되는지 GIF를 준비했습니다.


SplashActivity -> MainActivity (업데이트 코드 실행)



깔끔하게 업데이트 되는 모습! 이거 정말 좋아요! 두번 쓰세요! 세번 쓰세요!



필자가 진행한 상태 테스트

/**
* 업데이트가 없는 경우
* UpdateAvailability : 1
* installStatus : 0
*
*
* 권장 업데이트 테스트
*
* 업데이트가 있는 경우
* UpdateAvailability : 2
* installStatus : 0
*
* 업데이트 확인 후 진행 중
* UpdateAvailability : 3
* installStatus : 2
*
* 업데이트 다운로드 완료
* UpdateAvailability : 3
* installStatus : 11
*
* 업데이트 설치/재시작 진행 중
* (뒤로 가기 및 화면 종료를 해도 설치가 완료되면 앱을 자동 재실행 합니다.)
* UpdateAvailability : 3
* installStatus : 3
*
* 업데이트 설치 및 재시작 완료
* UpdateAvailability : 1
* installStatus : 0
*
*
* 즉시 업데이트 테스트
*
* 업데이트가 있는 경우
* UpdateAvailability : 2
* installStatus : 0
*
* 업데이트 확인 후 진행 중(확인 클릭 후 뒤로가기)
* UpdateAvailability : 3
* installStatus : 2
*
* 업데이트 다운로드 완료
* (설치 화면을 벗어나면 자동으로 재실행 되지 않습니다.)
* UpdateAvailability : 3
* installStatus : 11
*
* 업데이트 설치 및 재시작 완료
* UpdateAvailability : 1
* installStatus : 0
*
*/


반응형
Comments