Android - In App Update

김태웅·2020년 3월 14일
1

Android

목록 보기
1/2

인앱 업데이트 (In-App Updates)

코로나19가 확산되기 시작한 시점에 확진자들의 이동 경로를 한눈에 알기 쉽게 해준 코로나 맵 웹사이트가 사회적 이슈로 떠올랐고, 코로나맵 앱버전을 만들면 어떨까? 하는 생각에 코로나맵 안드로이드 앱을 만들기 시작하였다.

단, 많은 시간을 투자할 수 없었기에 딱 하루 혹은 이틀만 투자하여 만들어서 출시까지 생각했다. 물론 생각처럼 진행되지 못했다

이래저래 만들어서 출시는 하였지만 엄청난 수의 확진자가 쏟아져 나옴과 동시에 구글에서 앱을 중단해 버렸다. 잘못된 정보를 퍼트리지 못하게 하기 위한 조치라고 한다. 관련 기사

아직까지 앱을 출시해 본 적 없던 나는 잘못된 초기 기획으로 인해 사용자들에게 강제로 업데이트를 시켜줘야 하는 상황이 왔고, 사용자들에게 업데이트와 관련된 방법은 생각조차 하지 못했었다.

찾아보니 스플래시 화면에서 API를 통해 버전을 검사하고 Play Store로 이동해 주는 방법이라던지.. 많은 방법이 있다는 것을 알게 되었는데 2019년 Google I/O에서 발표한 인앱 업데이트 기능이 가장 혹했다.

안드로이드 개발자 레퍼런스
앱 안에서 업데이트를 진행 할 수 있다는 점이 일단 이쁘고, 신기하다.

유연한 업데이트 - 앱을 다운로드 하는동안 앱을 사용 가능한 경우, 핵심기능 업데이트가 아닌, 선택적 업데이트에 적합할듯

즉시 업데이트 - 앱을 무조건 업데이트 하고 사용 가능한 경우, 핵심기능 업데이트 시, 강제 업데이트에 적합할듯

적용해보자

강제 업데이트가 필요 했던 나는, 스플래시 부분에서 버전을 확인하고 업데이트를 하면 되겠다고 생각하고 적용하였다. (여기서는 즉시 업데이트만 설명한다, 하지만 유연한 업데이트도 적용이 어렵지 않을 듯 하다)

'Android 5.0(API 수준 21) 이상을 실행하는 기기에서만 작동하며 Play Core 라이브러리 1.5.0 이상 사용이 필수입니다.'

일단 app수준의 gradle에 implemantation먼저 해주자

implementation 'com.google.android.play:core:1.6.4'

전역변수 선언 해주고

private final int MY_REQUEST_CODE = 100;
private AppUpdateManager mAppUpdateManager;

먼저 업데이트가 가능한 상태인지 알기 위해 AppupdateManager를 사용한다

mAppUpdateManager = AppUpdateManagerFactory.create(getApplicationContext());

// 업데이트 사용 가능 상태인지 체크
Task<AppUpdateInfo> appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();

// 사용가능 체크 리스너를 달아준다
	appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
            @Override
            public void onSuccess(AppUpdateInfo appUpdateInfo) {
                if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                        && // 유연한 업데이트 사용 시 (AppUpdateType.FLEXIBLE) 사용
                        appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {

                    // 업데이트가 사용 가능한 상태 (업데이트 있음) -> 이곳에서 업데이트를 요청해주자
                } else {
                    
                    // 업데이트가 사용 가능하지 않은 상태(업데이트 없음) -> 다음 액티비티로 넘어가도록 
                }
            }
        });

업데이트가 사용 가능한 상태라면, 업데이트를 진행해주자 : 사용 가능 체크 If문 안에 넣었다

	try {
		mAppUpdateManager.startUpdateFlowForResult(
  	  		appUpdateInfo,
			// 유연한 업데이트 사용 시 (AppUpdateType.FLEXIBLE) 사용
			AppUpdateType.IMMEDIATE,
			// 현재 Activity
			SplashActivity.this,
			// 전역변수로 선언해준 Code
			MY_REQUEST_CODE);
	} catch (IntentSender.SendIntentException e) {
			Log.e("AppUpdater", "AppUpdateManager Error", e);
			e.printStackTrace();
	}

이렇게 인앱 업데이트는 사용 끝!
이제 업데이트를 성공적으로 실행했다면 업데이트 후 자동으로 넘어갈 것이다.
업데이트를 성공적으로 마치지 못했다면?
OnActivityResult에서 requestCode로 MY_REQUEST_CODE를 통해 검사한다.
나는 성공적으로 수행하지 못했을 시에
SnackBar로 사용자에게 알리고, finishAffinity()를 통해 앱을 종료하도록 하였다.

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == MY_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                Log.d("AppUpdate", "Update flow failed! Result code: " + resultCode); // 로그로 코드 확인
                showCustomSnackbar(findViewById(R.id.splash_main_view), "코로나 맵을 사용하기 위해서는 업데이트가 필요해요");  //snackbar로 사용자에게 알려주기
                finishAffinity(); // 앱 종료
            }
        }
    }

마지막으로 즉시 업데이트 사용시에는 업데이트 도중 앱을 stop 시켰다가 다시 실행 했을때 멈추진 않았는지, 검사를 통해 진행해줘야 한다.
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS를 사용해 체
이는 onResume에서 처리한다.

    @Override
    protected void onResume() {
        super.onResume();

        mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(
                new OnSuccessListener<AppUpdateInfo>() {
                    @Override
                    public void onSuccess(AppUpdateInfo appUpdateInfo) {
                        if (appUpdateInfo.updateAvailability()
                                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                            // 인 앱 업데이트가 이미 실행중이었다면 계속해서 진행하도록
                            try {
                                mAppUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.IMMEDIATE, SplashActivity.this, MY_REQUEST_CODE);
                            } catch (IntentSender.SendIntentException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
    }

이렇게 하면 인 앱 업데이트 적용 끝!

첫 번째 문제 ..

적용하기는 너무 간단했지만 테스트를 해볼 수 가 없었다.
FakeAppUpdateManager 통해 테스트를 한다던가, alpha버전 비공개 출시를 통해 한다던가 여러가지 방법이 있었지만,
이미 앱을 출시한 상황이라면 Build 버전을 마켓 버전보다 내려서 빌드 하는 것으로 테스트가 가능하다! 가장 빠르고 쉬운 방법이 아닌가 싶다.

또 다른 문제..

되는 경우가 있고, 안되는 경우가 있다.
업데이트가 되지 않는 경우, 다음과 같은 확인을 거치면 반드시 업데이트가 실행되는 것을 알 수 있었다

  1. Google Play 스토어 앱을 완전히 종료해야 합니다.
  2. Google Play 스토어 앱을 열고 내 앱/게임 탭으로 이동합니다.
  3. 테스트 중인 앱과 함께 사용 가능한 업데이트가 표시되지 않으면 올바로 테스트 트랙을 설정했는지 확인합니다.

스플래시 화면에서 실시간으로 확인해 주는 것이 아니라, 구글 플레이 스토어 앱을 통해 업데이트가 있는지, 없는지 여부를 받아오는 방식으로 작용하는 것같다.
나는 무조건 되게 해야했는데 ... ㅜㅜ

이래저래 어찌됐든 Google In-AppUpdate는 이렇게 마무리한다.

profile
Everything Counts

4개의 댓글

comment-user-thumbnail
2020년 7월 7일

질문 있습니다! 곧 앱을 출시하려고 하는데 gradle 에 있는 versioncode를 1로 해서 출시를 하면 제가 version코드를 0으로 낮춰서 테스트가 가능한가요?? 그리고 만약에 업데이트가 안될 시 이미 다운로드를 받은 사람들은 본인이 스스로 구글스토어에서 업데이트 하기 전까지 제가 어떻게 할 방법은 없는 건가요??

1개의 답글
comment-user-thumbnail
2020년 11월 3일

안녕하세요! 글 잘 읽었습니다. 설명해주신 대로 따라하니, 쉽게 구현할 수 있었습니다. 감사합니다.

질문이 있는데, 구글 플레이 스토어에 해당 앱의 상위 버전이 없는데도(=업데이트할 필요가 없는데도) 앱 실행 시 마다 매번 업데이트 문구가 뜨는데 이에 대한 해결법을 아시는 지 궁금합니다.

업데이트가 할 필요 없을 때는 addOnSuccessListener에서 else 문으로 빠진다고 생각했는데, 아닌가요ㅠㅠ 답변 부탁드립니다.

1개의 답글