본문 바로가기
안드로이드

APK 확장 파일 사용하기 - APK Expansion Files

by 호군 2014. 7. 21.
반응형

APK 확장 파일 APK Expansion Files




정확한 내용은 안드로이드 공식 문서를 보시기를 바랍니다.




왜 APK Expansion Files을 사용하는가?


현재 Google Play는 50MB 이하의 APK파일만을 허용하고 있다. 그래서 50MB를 초과하면 Google에서 제공하는 APK Expansion Files 서비스를 이용해서 업로드 할 수 있다.


꼭 Google에서 제공하는 APK Expansion Files 서비스를 이용할 필요는 없다. 코드와 리소스 정리를 통해서 APK 크기를 50MB 이하로 줄이거나 별도의 서버를 구축해서 사용해도 된다.


결론은 Play Store 업로드를 위해 APK 파일을 50MB 이하로 줄이면 OK!




어떤 제한이 있는가?


가장 큰 제한은 확장 파일을 최대 2개까지만 설정할 수 있고, 각각의 최대 파일 크기는 2GB까지라는 점이다. 

  • 최대 2개의 확장파일, 최대 4GB (각각 2GB)
  • only OBB 파일 (ZIP으로 올리면 Google Play에서 OBB로 자동 변환)




최종적으로 무엇이 필요한가?


APK Expansion Files 서비스를 이용하려면 몇 가지 준비가 필요하다. 가장 중요한 것은 Play Store 개발자 계정이다. 만약 계정이 없다면 테스트 조차할 수 없다. 그리고 APK Expansion Files에 대한 코드 수정을 한 후 나온 결과물인 *.apk 파일과 리소스 파일들을 담은 zip 파일이다.

  • Play Store 개발자 계정
    - Play Store에 어플리케이션을 생성하기 위해서 필요함

  • 리소스가 분리된 APK 파일
    - 최종 결과물

  • 리소스가 포함된 ZIP 또는 OBB 파일
    최종 결과물
    - 일반적으로 assets/ 에 위치한 리소스 파일들의 묶음
    - 디바이스에 압축을 풀지 않고 OBB에서 바로 읽으려면 압축률 0으로 압축




개발에 필요한 라이브러리는?


구글에서 제공하는 라이브러리 Google APK Expansion과 Google Play Licensing 라이브러리를 사용한다. 해당 라이브러리는 Android SDK Manager를 통해서 다운로드 할 수 있다.

  • Google APK Expansion
    - Downloader Library    : Play Store에서 실재로 다운로드하는 라이브러리
    - Zip File Library           : ZIP파일을 읽을 수 있는 라이브러리
    - Downloader Sample : Downloader Library를 사용하는 예제 코드

  • Google Play Licensing
    - Play Licensing Library : Play Store로부터 확장파일을 얻기 위한 라이브러리




적용하기


구글이 제공하는 예제를 참고하면 APK Expansion Files를 쉽게 적용할 수 있다. 여기서는 예제를 그대로 가져와서 적용하려고한다. 그러면 가져와야하는 파일과 내용들을 확인해보자.

  • AndroidManifest.xml
    - 권한, 액시티비, 서비스, 브로드캐스트 리시버 정의

  • layout/main.xml, values/strings.xml
    - 다운로드 화면 레이아웃 및 문자열 정의

  • SampleAlarmReceiver.java, SampleDownloaderActivity.java, SampleDownloaderService.java
    - 다운로드 진행 화면 업데이트 및 업데이트 필요성 판별


AndroidManifest.xml
하이라이트된 영역을 현재 프로젝트로 가져오면 된다.  XML코드를 조금 살펴보면 Activity의 카테고리가 android.intent.category.LAUNCHER 인 것을 볼 수 있다. 초기에 앱이 시작할 때 파일이 존재하는지 확인해야하기 때문에 해당 액티비티를 가장 먼저 실행하도록 한다.

AndroidManifest.xml


    
    

    
    
    
    
    
    
    
    
    
    
    
    

    
        
            
                
                
            
        

        
        

        
        

    



layout/main.xml, values/strings.xml

이 두 리소스 파일은 원하는 이름으로 변경해서 가져온다.

  • layout/main.xml → layout/downloader.xml

  • values/strings.xml → values/strings_downloader.xml

values/strings_downloader.xml 파일에서 app_name은 삭제한다.


SampleAlarmReceiver.java

이 소스코드는 아무 변경없이 그대로 사용한다. SampleAlarmReceiver는 AlarmService를 이용해서 다운로드 서비스가 죽지않고 유지되록 한다.


SampleDownloaderService.java

이 소스코드에서는 BASE64_PUBLIC_KEY를 변경해야한다. 이 키값은 Play Store에서 해당 어플리케이션의 "라이선스 및 인앱 결제" 키이다. SampleDownloaderService는 실제로 다운로드를 수행한다.


SampleDownloaderActivity.java

이 소스코드에서는 R.layout.main을 원하는 이름으로 변경했던 R.layout.downloader로 변경한다. 그리고 OBB파일이 유효하다고 생각한다면 기존의 Main Activity를 실행한다. 수정한 부분의 코드를 확인해보자.


SampleDownloaderActivity.java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /**
     * Both downloading and validation make use of the "download" UI
     */
    initializeDownloadUI();

    /**
     * Before we do anything, are the files we expect already here and
     * delivered (presumably by Market) For free titles, this is probably
     * worth doing. (so no Market request is necessary)
     */
    if (!Util.expansionFilesDelivered()) {

        try {
            Intent launchIntent = ExpansionDownloaderActivity.this
                    .getIntent();
            Intent intentToLaunchThisActivityFromNotification = new Intent(
                    ExpansionDownloaderActivity
                    .this, ExpansionDownloaderActivity.this.getClass());
            intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());

            if (launchIntent.getCategories() != null) {
                for (String category : launchIntent.getCategories()) {
                    intentToLaunchThisActivityFromNotification.addCategory(category);
                }
            }

            // Build PendingIntent used to open this activity from
            // Notification
            PendingIntent pendingIntent = PendingIntent.getActivity(
                    ExpansionDownloaderActivity.this,
                    0, intentToLaunchThisActivityFromNotification,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            // Request to start the download
            int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                    pendingIntent, ExpansionDownloaderService.class);

            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // The DownloaderService has started downloading the files,
                // show progress
                initializeDownloadUI();
                return;
            } // otherwise, download not needed so we fall through to
              // starting the movie
        } catch (NameNotFoundException e) {
            Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
            e.printStackTrace();
        }

    } else {
        //validateXAPKZipFiles();
        Util.startAppActivity();
        finish();
    }

}
SampleDownloaderActivity.java
protected void onPostExecute(Boolean result) {
    if (result) {
        mDashboard.setVisibility(View.VISIBLE);
        mCellMessage.setVisibility(View.GONE);
        mStatusText.setText(R.string.text_validation_complete);
        mPauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Util.startAppActivity();
                finish();
            }
        });
        mPauseButton.setText(android.R.string.ok);
    } else {
        mDashboard.setVisibility(View.VISIBLE);
        mCellMessage.setVisibility(View.GONE);
        mStatusText.setText(R.string.text_validation_failed);
        mPauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        mPauseButton.setText(android.R.string.cancel);
    }
    super.onPostExecute(result);
}

Util.java
public class Util {
    public static void startAppActivity() {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setClassName(AppApplication.getAppContext().getPackageName(), "com.dhna.example.xapk.AppActivity");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        AppApplication.getAppContext().startActivity(intent);
    }
}







APK Expansion Files 테스트하기


Play Store에서 "앱 출시"는 약간의 망설임을 준다. 앱 출시를 하는 순간 더 이상 이 어플리케이션은 Play Store에서 지울 수가 없고 단순히 감추기만 가능하다. 그런데 APK Expansion Files를 테스트하려면 "앱 출시"를 해야한다. 즉, Play Store에서 앱을 다운로드 받을 수 있는 상태여야 테스트가 가능하단 의미이다. 테스트는 앱을 출시한 후 약 2시간 이후에 할 수 있다.


위 내용은 한글로된 문서를 제공하고 있고, 여기에서 확인이 가능하다.


  1. Play Store Developer Console에 접속한다.

  2. 어플리케이션을 하나 생성한다.

  3. APK, 스토어 등록정보, 가격 및 배포에 정보를 채운다.

  4. 알파 테스트에 APK 파일을 업로드한다. (현재는 처음 업로드시 확장파일을 물어보지 않았음)

  5. 다시 알파 테스트에서 APK 파일 업로드를 누르고 APK파일과 확장파일을 업로드한다.
    - 확장파일을 ZIP파일로 올리면 앱 버전에 맞게 이름이 변경된다.

  6. 베타 테스터를 등록한다.
    - Google Groups나 Google Plus를 이용해서 테스터를 등록할 수 있다.

  7. 앱을 출시한다.

  8. 약 2시간 후 다운로드 URL로 접속한 후 앱을 다운로드한다.
    - "테스트 목록 관리"에 앱 다운로드 주소는 나와있다.
    - 주소는 HTTPS 형식이다. https://play.google.com/apps/testing/<PACKAGE_NAME>








반응형