안드로이드 IAB .. 계속..

In-app Billing Messages

사용자가 구매를 시작하면 니 앱은 구매 메시지를 간단한 IPC 콜을 이용하여 구글 플레이의 구매 서비스(MarketBillingService) 에 보낸다. 구글 플레이 어플은 모든 빌링 요청에 동시에 응답하며, 상태 노티와 다른 정보를 제공한다. 또한 어떤 요청에는 비동기적으로 에러 메시지와 자세한 결재 정보를 보낸다. 다음은 니 어플과 구글 플레이 어플 사이의 요청-응답 메시지의 기본을 서술한다.

In-app billing request

구매 요청은 MarketBillingService 인터페이스에 노출되 있는 sendBillingRequest() 를 불러 시작. 이 인터페이스는 Android Interface Definition Language 파일(IMarketBillingService.aidl)에 정의되어 있음. 다운로드 가능. 이 메서드는 한개의 <번들> 인수를 갖고 있음. 니가 전달하는 번들은 여러개의 키-밸류를 갖고 있음. 구매 타잎, 아이템, 타잎, 어플 정보.  더 자세한 것은 문서 참조.. 가장 중요한 키는 BILLING_REQUEST 키. 이 키는 구매 타잎을 정의하게 해 줌.  구글 플레이의 빌링 서비스는 다음의 다섯가지 종류의 요청을 지원함. > CHECK_BILLING_SUPPORTED 이 요청은 구글플레이 어플이 구매를 지원하는 걸 확인. 이 것은 어플이 시작할 때 전송. 이것은 결재와 관련된 UI 를 온/오프할 때 유용. > REQUEST_PURCHASE 이 요청은 구글플레이 어플에 구매 메시지를 전달하고 구매의 기초가 됨. 사용자가 구매 의사를 표현하면 이 요청을 보냄. 구글 플레이가 그 다음 체크아웃 UI 를 보여주고 금융결재를 다룬다. > GET_PURCHASE_INFORMATION 이 요청은 구매 상태 변화의 세부를 갱신한다. 구매는 요청된 구매가 성공적으로 발행 되거나, 사용자가 체크아웃 동안 취소하면 상태를 바꾼다. 이전 구매가 환불되도 일어난다. 구글 플레이는 구매 상태가 변하면 니 어플에 노티해서 갱신할 결재 자료가 있으면 이 요청만 보내면 된다. > CONFIRM_NOTIFICATIONS 이 요청은 어플이 구매상태 변화의 상세를 받았다는 것을 수락한다. 구글플레이는 구매상태변화 노티를 니가 그것을 받았다고 확인할 때까지 니 어플에 보낸다. > RESTORE_TRANSACTIONS 이 요청은 “managed purchases” 와 “subscriptions” 의 결재 상태를 갱신한다. 이 요청을 사용자의 결재 상태를 갱신할 필요가 있을 때 – 어플이 재설치 되었을 때, 혹은 처음 인스톨 될 때 – 보낸다.

In-app billing Responses

구글 플레이 어플은 동기/비동기 방식으로 작동.  동기 응답은 다음 세가지 키와 함께하는 ‘번들’ 임.

>RESPONSE_CODE : 이 키는 요청에 대한 상태 정보와 정보를 제공한다.

> PURCHASE_INTENT : 체크아웃 활동을 런치하는 데 쓰이는 PendingIntent 를 제공.

> REQUEST_ID : 이 키는 요청 아이디를 제공. 비동기 응답에 사용 됨.

이 키 중 일부는 모든 요청에 관련된 것은 아니다.
비동기 응답은 individual broadcast intents 의 형태로 보내지며 다음을 포함한다.

> com.android.vending.billing.RESPONSE_CODE
이 응답은 구글플레이 서버 응답코드를 품으며 니가 빌링 요청을 한 이후에 보내진다. 서버 응답 코드는 빌링 요청이 성공적으로 구글 플레이에 전달됐다거나 에러가 났다는 것을 가리킨다. 이 응답은 구매 상태 변화를 보고하는데는 쓰이지 않는다. (환불 / 구매 정보 등의)

> com.android.vending.billing.IN_APP_NOTIFY
이 반응은 구매가 성공했거나, 취소, 환불 되는 등의 상태 변화를 가리킴. 이 응답은 하나 이상의 노티 아이디를 포함. 각각의 노티 아이디는 특정 서버단 메시지와 대응되며 각 메시지는 하나 이상의 결재에 대한 정보를 포함. 어플이 IN_APP_NOTIFY broadcast intent 를 받은 다음 니는 GET_PURCHASE_INFORMATION 요청을 노티 아이디와 함께 보내 메시지 상세를 갱신한다.

> com.android.vending.billing.PURCHASE_STATE_CHANGED
이 응답은 하나 이상의 결재에 대한 정보를 포함. JSON 스트링으로 표현됨. 이 스트링은 사인되고, 그 서명은 어플에 암호화되지 않은 JSON 으로 보내진다. 빌링 메시지의 보안을 확실히 하기 위해, 어플은 이 스트링의 서명을 확인할 수 있다.
PURCHASE_STATE_CHANGED intent 와 한께 돌아온 JSON 스트링은 하나 이상의 빌링 결재의 상세를 어플에 제공한다. 아래는 그 예.

{ “nonce” : 134123412342134,

“orders” :  …… }

Messaging sequence

전형적인 구매 요청에 대한 메시징 절차는 그림 2에 있음.  요청 종류는 볼드체로 표기, broadcast intent는 이탤릭 표기. 그림 2에는 RESPONSE_CODE broadcase intents 는 모든 요청에 보내지므로 생략 했음.

기본 메시지 절차는 아래와 같음.

1. 어플이 구매 요청을 보낸다. product ID, 다른 인수들 명시.  (REQUEST_PURCHASE type)

2. 구글 플레이 어플이 다음의 키를 갖는 번들을 어플로 보냄 : RESPONSE_CODE, PURCHASE_INTENT, REQUEST_ID. PURCHASE_INTENT 는 PendingIntent 를 제공하는데, 이는 어플이 체크아웃 UI 를 시작하는 데 쓰인다.

3. 어플이 pending intent 를 런치하면 이는 체크아웃 UI 를 런치한다. [ 주의 : pending intent 를 activity context 로부터 런치해야함. application context 아님 ]

4. 체크아웃 절차가 끝나면 (구매가 성공되거나 취소) 구글 플레이는 어플에 노티메시지( IN_APP_NOTIFY broadcast intent) 를 보냄. 노티 메시지는 노티 ID 를 포함. 이는 결재를 참조함.

5. 어플이 GET_PURCHASE_STATE_CHANGED 요청을 보냄으로 결재 정보를 요청. 이때 결재를 위해서 노티 ID 를 명시.

6. 구글 플레이 어플이 RESPONSE_CODE key 와 REQUEST_ID 키와 함께 번들을 보낸다.

7. 구글 플레이 어플이 결재 정보를 PURCHASE_STATE_CHANGED broadcast intent 안에 넣어 어플에 보낸다.

8. 어플이 받았다는 메시지를 보낸다. (CONFIRM_NOTIFICATIONS type)

9. 구글 플레이 어플이 어플에 번들을 보낸다.

결재 정보를 받으면 (8번) 확인 메시지를 보내야 한다. 보내지 않으면 IN_APP_NOTIFY메시지를 계속 보낼 것임.  연습삼아, 아이템을 유저에게 잔달할 때까지 확인 노티를 주지 말아라. 이렇게 함으로써 어플이 죽거나 제품 전달에 실패한 경우 어플이 IN_APP_NOTIFY 메시지를 받아서 구매를 완결할 수 있다. 또한 최고의 연습으로, IN_APP_NOTIFY메시지를 다룰 수 있어야 한다.

결재를 복원하는 요청은 그림 3에 있음.

요청은 세 응답을 발생시킨다.  첫째는 번들, 다음은 RESPONSE … 이것은 상태정보, 에러정보를 포함. RESPONSE_CODE 메시지.

RESTORE_TRANSACTIONS 요청 타잎은 (구매 요청시 보내진 결재 정보와 같은 종류의) PURCHASE_STATE_CHANGED broadcast intent 를 촉발.

Handling IN_APP_NOTIFY messages

보통, 앱은 REQUEST_PURCHASE 메시지에 대한 응답으로  IN_APP_NOTIFY broadcast intent 를 구글 플레이로부터 받는다.  IN_APP_NOTIFY broadcast intent는 어플에게 요청한 구매 상태가 변경되었다는 것을 알려준다. 그 구매의 상세를 갱신하기 위해서, 어플은 GET_PURCHASE_INFORMATION 요청을 보낸다. 구글 플레이는 PURCHASE_STATE_CHANGED b. i. 로 응답하고 이는 구매상태변화의 상세를 답고 있다. 어플은 CONFIRM_NOTIFICATIONS 메시지를 보내서 구글 플레이에게 구매상태변경 정보를 받았다는 것을 알려준다.

몇몇 특별한 경우, 영수증을 컨펌 해도 여러개의 IN_APP_NOTIFY 메시지를 받거나, 구매를 시작하지도 않았는데, IN_APP_NOTIFY 를 받을 수 있다. 어플은 이 두가지 경우를 다룰 수 있어야 한다.

Handling multiple IN_APP_NOTIFY messages

구글 플레이가 주어진 PURCHASE_STATE_CHANGED 메시지에 대하여 CONFIRM_NOTIFICATIONS 메시지를 받으면 보통은 IN_APP_NOTIFY intent 를 보내는 것을 멈춘다. 하지만, 가끔 구글 플레이가 이것을 반복할 수 있다. 이것은 기기가 네트워크 연결이 끊겼을 때 일어날 수 있다. 이 경우 CONFIRM_NOTIFICATIONS 메시지를 받지 못했을 수 있고, 니가 결재 메시지를 받았다는 것을 알 때까지 계속 보낼 수 있다.
그러므로, 다음에 오는 그 메시지가 직전 거래의 결과라는 것을 알수 있어야 한다. 이것은 orderID를 확인함으로써 가능하다.

Handling refunds and other unsolicited IN_APP_NOTIFY messages

REQUEST_PURCHASE 메시지를 보내지 않아도 IN_APP_NOTIFY 를 받는 경우는 두가지가 있다. 그림 5 참조.

첫번째 경우는, 유저가 두 기기에 어플을 설치하고 한 기기에서 구매를 했을 경우. 이 때, 구글 플레이는 IN_APP_NOTIFY 메시지를 두번째 기기에 보내서 구매 상태가 변했다는 것을 알려준다. 니 어플은 이 메시지를 일반 구매와 동일하게 해석해서  PURCHASE_STATE_CHANGED b. i. 메시지를 받아 어느 아이템을 구매했는지 알수 있어야 한다.  이것은 구매 종류가 “managed per user account” 일 경우에만 해당한다는 것을 의미한다.

두번째 경우, 구글 플레이가 구글 지갑으로부터 환불 노티를 받는 경우 IN_APP_NOTIFY 를 받을 수 있다. 이 경우, 구글 플레이는 어플에 IN_APP_NOTIFY 메시지를 준다. 어플은 이 메시지를 해석하여 궁극적으로 PURCHASE_STATE_CHANGED 메시지를 받도록 한다. 환불 정보는 JSON 스트링에 있다. 또한, purchaseState 필드가 2로 세팅된다.

Security Controls

어플로 보내지는 결재 정보의 안전을 위해 구글 플레이는 PURCHASE_STATE_CHANGED b. i. 가 담고 있는 JSON 스트링에 사인한다. 구글 플레이는  이 서명을 만들기 위한 당신의 퍼블리셔 계정과 연결된 private key를 이용한다.  이와 쌍을 이루는 public key 부분은 계정의 프로파일 페이지에 있다. 이는 구글 플레이 라이센싱과 함께 사용되는 동일한 키이다.

구글 플레이가 빌링 응답에 사인하면, 서명된 JSON 스트링과 서명을 포함한다. 어플이 이 서명된 응답을 받으면 RSA 키 쌍의 public key 를 이용하여 서명을 확인할 수 있다. 서명 확인을 함으로써 조작되거나, 위조된? 응답을 탐지할 수 있다. 이 서명 확인 단계는 어플에서 할 수 있다; 하지만, 서버가 있다면 서버에서 하는 것을 권한다.

IAB 은 또한 nonces (한번 쓰는 난수) 를 사용하여 구글플레이로부터 오는 구매 정보의 안전성을 확인하는 것을 돕는다. 어플은 넌스를 발생하고 GET_PURCHASE_INFORMATION 요청과 RESTORE_TRANSACTIONS 요청과 함께 보내야 한다. 구글 플레이가 요청을 받으면 ‘넌스’를 JSON 에 추가한다. 그러면, JSON 스트링이 사인되고 어플로 되돌아온다. 어플이 JSON 을 받으면 ‘넌스’를 확인할 필요가 있다.

In-app Billing Requirements and Limitations

> IAB는 구글 플레이를 통해 퍼블리쉬된 어플에만 이식할 수 있다.

> 구글 지갑 계정이 있어야 한다.

> 안드로이드 마켓 어플 2.3.4 이상. ….

> 안드로이드 1.6 이상.

> 디지털 콘텐츠에만 해당. 실제 제품, 서비스 등은 불가.

> 구글 플레이에서는 콘텐츠 전달은 하지 않음.

> 네트워크 필요.

안드로이드 IAB ..

In-app Billing Overview

In app Billing Service 에서 구글 서버에 요청, 확인 하는 과정 대리함. 금융 거래 데이터를 다룰 필요가 없다.

Product and Purchase Types

먼저 제품을 등록해야 함. 제품 종류, 구입 종류, SKU, 가격, 설명 등

Product Types

두가지 상이한 제품이 있지만, 구입 과정은 동일한 통신모델, 데이터구조, UI 를 갖는다.

In-app products

한번만 구매하는 제품. 기능 언락. 구매하면 반품 윈도우가 없다.  환불 받으려면 개발자에게 직접 연락해야 함.
“managed per user account” 또는 “unmanaged” 를 통해 판매 가능.
항상 ‘하나의’ 앱과 명시적으로 연관되어야 함. 즉, 한 앱은 다른 앱을 위해 발행된 ‘인앱 제품’ 을 구매할 수 없다.
In app products 는 모든 버전의 In-app Billing 에서 지원한다.

Subscriptions

일정 주기로 판매되는 아이템.  유저가 이것을 구입하면 구글 플레이와 그 결재 프로세서가 자동적으로 결재를 한다. 한번 구매하면 구글 플레이는 계정을 승인 요청이나 다른 경고 없이 무한히 계속한다. 사용자는 언제고 구매를 취소할 수 있다.
“managed per user account” 구매 방식에 의해서만 판매 가능. 이것도 리펀드 윈도우 없음. 개발자 직접 연락.  자세한 사항은 더 참조할 것.

Purchase Types

두가지 구매 방식. “유저 계정” 에 따른 것과, “비관리” 되는 것.

Managed per user account : 계정별 관리

한번만 구매하는 제품. 구매 되면 구글 플레이는 영수증을 ‘누가’ 구매 했는지 영원히 보관한다. 이것을 통해 나중에 구글에 쿼리하여 구매한 아이템을 복원 할 수 있다.  이미 구매한 아이템을 구매하려고하면 구글 플레이가 재구매를 박고 “Item already purchased” 에러를 보낸다.
게임 레벨, 기능 등을 팔 때 유용하다. 이 아이템은 일시적인 것이 아니고 앱을 재 설치할 때 복원되어야 한다.

Unmanaged : 비관리 형식

구글 플레이에 저장되지 않는 아이템들. 나중에 쿼리할 수 없다. 개발자가 결재 정보를 관리할 책임이 있다. 구글 플레이가 재구매를 막지 않기때문에 여러번 구매할 수 있으므로 몇 번 구매 가능한 지에 대해서 개발자가 관리해야 한다.
소비성 제품 판매에 적절하고 여러번 구매될 수 있다.

In-app Billing Architecture

기기에 설치된 구글 플레이 앱에 노출된 API 를 이용하여 당신의 앱은  In-app Billing 서비스에 접속한다. 구글 플레이 앱은 비동기 메시지 루프를 통해 구매 요청을 전달하고 앱과 구글 플레이 서버 사이에서 신호를 받는다.
실제로, 당신의 앱은 구글 플레이 서버와 직접 통신하지 않는다. 대신, 니 앱은 구글 플레이 어플에 IPC 로 요청하고 구글 플레이 어플로부터 비동기 브로드캐스트 인텐츠의 형식으로 응답을 받는다.  asynchronous broadcast intents.
니 앱은 어떠한 네트워크 커넥션도 하지 않으며 특별한 안드로이드 플랫폼의 API 를 이용하지도 않는다.

어떤 경우 사설 원격 서버를 사용해서 사진이나 미디어를 전달할 수 도 있다. 또한, 거래 내역을 저장하거나 추가적인 보안 작업을 할 수도 있다. 앱에서 보안관련 처리를 할 수 있지만 서버에서 하는 것이 더 안전하다.

전형적인 in-app billing 구현은 3가지 요소에 달려있다.

>> A Service (BillingService) : 어플로부터 구매 메시지를 처리, 구매 요청을 구글 플레이 서비스에 보냄.

>> A BroadcastReceiver (BillingReceiver) : 구글 플레이 어플로부터 모든 비동기 구매 응답을 받음.

>> A security component (Security) : 보안 관련 작업. signature verification, nonce generation.

추가 가능한 다른 두 요소.

>> A response Handler : 구매 노티, 에러, 상태 메시지 등.
>> An observer : 니 어플에 콜백을 보내는 일 담당.

이 외에 어플에서는 사용자 구매를 저장할 방법을 제공해야 하고, 구매 관련 유아이가 있어야 함. 체크아웃 UI 는 불필요. 사용자가 구매를 시작할 때, 구글 플레이 어플은 UI 를 제공한다. 유저가 체크아웃을 완료하면 어플이 재개 됨.

안드로이드 구글 플레이어 올리기..

안드로이드는 50메가 제한이 있음..

.. 어떻게 유니티에서 파일을 나눠서 apk 파일을 만들 것인가 ?  ==> 애셋 번들을 만들라.  방법 잘 정리된 블로그 글.

.. 어떻게 나눠진 파일을 올리는가 ?

zip 으로 압축해서 올림.  android\obb\패키지명\main.버전코드.패키지명.obb 이런식으로 된다고?..  파일명은 신경 꺼라.  바로가기

안드로이드 개발자 사이트 번역

APK Expansion Files (확장파일)

구글 플레이는 현재 50메가 이하의 파일을 요구한다. 대부분의 어플에게 이 용량은  코드와 애셋을 합쳐 충분할 것이다. 하지만 고퀄리티 그래픽이나 미디어 파일, 큰 애셋을 갖는 어플은 더 많은 공간을 요구한다. 이전에는 50메가 이상의 경우 사용자가 앱을 오픈할 때  개발자가 추가적인 리소스를 호스팅하고 다운로드를 제공해야 했다.  추가적인 파일을 호스팅하는 것은 비용이 들고 UX 도 이상적이지 않다. 이 과정을 쉽게하기 위해 구글 플레이는 2개의 대용량 파일을 붙일 수 있게 했다.

구글 플레이는 확장파일을 비용 없이 제공한다.  확장파일은 기기의 공유 스토리지 위치 (SD 카드 또는 USB-마운터블 파티션 – 즉 “외장” 공간) 에 저장된다. 대부분의 기기에서 구글 플레이는 확장 파일을 APK 파일 다운로드와 동시에 받아 사용자가 처음 열었을 때 모든 것이 다 준비되도록 한다.  하지만, 어떤 경우에는 어플이 구글 플레이로부터 어플 시작 시 파일을 다운로드 해야 한다.

Overview

구글 플레이 안드로이드 개발 콘솔을 통해서 APK를 올릴 때 마다 하나 또는 두개의 확장파일을 추가하는 옵션이 주어진다. 각 파일은 2기가 까지이며 어떤 포맷이든 허용되나 다운로드 시 bandwidth 를 유지할 수 있는 압축된 파일을 추천한다. 개념적으로 각 확장파일은 다른 역할을 한다.

# ‘main’ 확장파일은 추가적인 리소스에 대한 주된 확장파일이다.

# ‘patch’ 확장파일은 메인 파일에 대한 부가적인 파일로 작은 업데이트 용도의 파일이다.

두 확장 파일을 어떤 방법으로든 쓸 수 있지만, 메인 파일이 중요한 애셋을 포함하고 업데이트는 가끔 하도록 권장한다. 패치 파일은 더 작고 패치를 위한 파일..

하지만, 어플이 새 패치 파일만을 업데이트 했더라도, 개발자는 매니패스트 의 versionCode 를 업데이트 해서 새로운 APK 를 올려야 한다.

\/\/ Note : 패치 확장파일은 실질적으론 메인 확장 파일과 동일함. 어떻게 사용해도 무방. 시스템은 앱의 패칭을 위해 패치 확장파일을 사용하지 않음. 패치는 스스로 해야 함.

File name format

각 확장 파일은 zip, pdf, mp4 등 어떤 포맷도 가능하다. 파일 타입에 관계 없이 구글 플레이는 opaque binary blobs 로 간주하고 다음과 같은 기준으로 리네임한다.

[main|patch].<expansion-version>.<package-name>.obb

세가지 콤포넌트.

main  or  patch
파일이 메인인지 패치인지 구별. 각 APK 에 하나의 메인과 하나의 패치만 가능.

<expansion-version>
확장이 ‘첫번째’ 참조되는 정수의 버전 코드  . (어플의 android:versionCode 값과 일치)
‘첫번째’ 는 개발자 콘솔이 업로드된 확장 파일을 재사용하는 것(새로운 APK 파일과 함께)을 허용하기 때문에 강조되었음.  이것은 처음 파일을 올렸을 때의 버전을 유지한다.

<package-name>
어플의 자바-스타일 패키지 이름.

예>   main.314159.com.example.app.obb  이렇게 됨.

Storage location

구글 플레이가 확장파일을 다운로드 할 때, 시스템의 공유 공간에 저장한다. 제대로 작동되기 위해서 파일을 지우거나, 옮기거나, 이름을 바꾸지 말것. 어플이 구글 플레이에서 다운로드 해야 할 때, 사용자는 바로 같은 곳에 저장해야 한다.

정해진 위치는 다음과 같다.

<shared-storage>/Android/obb/<package-name>/

# <shared-storage> 는 공유 공간이며 getExternalStorageDirectory() 로 받는다.

# <package-name>  은 getPackageName() 으로 얻음.

각 어플에는 메인/패치 파일이 가능. 이전 버전은 새로운 확장 파일로 업데이트하면 overwrite 됨.

파일을 언-팩 해야하면 .obb 파일을 지우지 말고 같은 디렉토리에 풀린 데이터를 저장하지 말라.  압축 해제된 파일은 getExternalFilesDir() 에 지정된 곳에 저장해야 한다. 하지만, 가능하면 해제 과정 없이 확장 파일을 바로 사용하는 것이 제일 좋다. 예를 들면 집 파일을 바로 읽는 라이브러리 같은 경우..

/\/\ Note : APK 파일과는 다르게 어떤 파일이라도 저장 가능하다.

/\/\ Tip : 미디어 파일을 집 압축 한다면 미디어 플레이백 콜을 오프셋, 길이 조절로 부를 수 있다. (MediaPlayer.setDataSource() &  SoundPool.load() ) 압축 해재 없이. 이 과정을 통해 부가적인 압축을 하지 않아도 된다. 예를 들어 집 툴을 쓸 때 -n 옵션을 써서 압축을 안 할 수 있다.

Download process

대부분의 경우 확장 파일은 APK 파일과 함께 다운로드, 저장 된다. 하지만, 어떤 경우에은 구글 플레이는 확장 파일을 다운로드 하지 못하거나 사용자가 이전에 내려받은 파일을 지울 수 있다. 이러한 경우에 대비하여 어플은 파일 자체를 다운로드할 수 있어야 한다.  구글 플레이가 제공하는 URL 을 통해서.

다운로드 프로세스

1. 사용자가 구글 플레이에서 앱 선택.
2. 구글 플레이가 확장파일을 다운로드 할 수 있으면 (대부분의 경우) APK 와 함께 파일 다운로드. 확장 파일을 다운로드 할 수 없을 때는 APK 만 한다.
3. 사용자가 앱을 런칭할 때, 앱은 확장 파일이 있는 지 확인해야 함.  있으면 레디 투 고. 없으면 구글 플레이의 HTTP 에서 다운로드 해야 함. 앱은 구글 플레이 클라이언트에 Application Licensing 서비스를 이용하여 요청해야 한다. 이것은 이름, 파일 크기, URL 에 대응함. 이 정보로 파일을 다운로드 하고 정당한 위치에 저장한다.

주의 : 파일 다운로드 코드를 포함하는 것이 중요하다. 라이브러리 제공했으니 최소의 작업량으로 가능할 것.

Development checklist

내용 요약

1. 50메가 이상이 필요한 지 판단. 가능한 한 작게.. 여러 기기를 위해서라면 복수의 APK  를 고려하라.
2. 메인 확장 파일로 뭘 뺄 지 결정.
3. 기기의 ‘공유 저장 위치’ 로부터 읽어 들이도록 개발. 파일 포맷이 무관하면 ‘집’ 해서 APK Expansion Zip Library 를 이용하도록.
4. 초기에 확장 파일 유무 검사. 없으면 다운로드 받도록. Downloader Library  를 사용하면 간단하다.
테스팅.

Rules and Limitations