Play 기능 전송

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Play 기능 전송을 사용하여 앱에서 다음 기기에 주문형 기능 모듈을 다운로드할 수 있습니다.

  • Android 5.0(API 수준 21) 이상을 실행하는 휴대기기 및 태블릿
  • Chrome OS 기기

앱에서 간단히 Play Core 라이브러리의 API를 호출하여 필요에 따라 이러한 모듈을 다운로드하고 설치하기만 하면 됩니다. Google Play 스토어는 이 모듈에 필요한 코드와 리소스만 기기에 푸시합니다. 이 API를 사용하여 Android 인스턴트 앱의 주문형 모듈을 다운로드할 수도 있습니다.

먼저 프로젝트에 기능 모듈을 추가하고 주문형으로 사용할 수 있도록 설정하는 방법을 알아보려면 기능 모듈 만들기를 참고하세요.

또한 이 가이드를 읽은 후 Play Core API 샘플 앱을 사용하여 작동 중인 API를 확인하고 인앱 업데이트를 지원하는 방법을 알아보세요.

마지막으로, 앱을 게시하기 전에 App Bundle을 테스트하여 앱의 주문형 기능이 의도한 대로 작동하는지 확인합니다.

프로젝트에 Play Core 라이브러리 포함

시작하기 전에 먼저, 프로젝트에 Play Core 라이브러리를 가져와야 합니다.

주문형 모듈 요청

앱이 기능 모듈을 사용해야 하는 경우 앱이 포그라운드에 있는 동안 SplitInstallManager 클래스를 통해 기능 모듈을 요청할 수 있습니다. 요청할 때 앱은 타겟 모듈 매니페스트의 split 요소에서 정의한 대로 모듈의 이름을 지정해야 합니다. Android 스튜디오를 사용하여 기능 모듈을 생성할 때 빌드 시스템은 개발자가 제공한 모듈 이름을 사용하여 컴파일 시 모듈의 매니페스트에 이 속성을 삽입합니다. 자세한 내용은 기능 모듈 매니페스트를 참고하세요.

예를 들어 기기의 카메라를 사용하여 사진 메시지를 캡처하고 전송하는 주문형 모듈이 있는 앱을 생각해 보세요. 이 주문형 모듈은 매니페스트에 split="pictureMessages"를 명시한다고 가정해 보겠습니다. 다음 샘플은 SplitInstallManager를 사용하여 일부 프로모션 필터의 추가 모듈과 함께 pictureMessages 모듈을 요청합니다.

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

자바

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

앱이 주문형 모듈을 요청하면 Play Core 라이브러리는 'fire-and-forget' 전략을 사용합니다. 즉, 모듈을 플랫폼에 다운로드하라는 요청을 전송하지만 성공적으로 설치되었는지는 모니터링하지 않습니다. 설치 후 사용자가 계속 진행하게 하거나 적절하게 오류를 처리하려면 요청 상태를 모니터링해야 합니다.

참고: 기기에 이미 설치된 기능 모듈을 요청해도 괜찮습니다. API는 모듈이 이미 설치되어 있음을 감지하면 즉시 요청이 완료된 것으로 간주합니다. 또한 모듈을 설치한 후에는 Google Play에서 자동으로 업데이트합니다. 즉, App Bundle의 새 버전을 업로드하면 플랫폼은 앱에 속한 설치된 APK를 모두 업데이트합니다. 자세한 내용은 앱 업데이트 관리를 참고하세요.

모듈의 코드 및 리소스에 액세스할 수 있으려면 앱에서 SplitCompat을 사용 설정해야 합니다. Android 인스턴트 앱에는 SplitCompat이 필요하지 않습니다.

주문형 모듈의 설치 연기

앱에서 주문형 모듈을 즉시 다운로드하고 설치할 필요가 없다면 앱이 백그라운드에 있는 동안 설치를 연기할 수 있습니다. 예를 들어 앱의 향후 출시를 위해 일부 프로모션 자료를 미리 로드하려는 경우입니다.

아래와 같이 deferredInstall() 메서드를 사용하여 나중에 다운로드될 모듈을 지정할 수 있습니다. 그리고 SplitInstallManager.startInstall()과 달리 지연 설치 요청을 시작하기 위해 앱이 포그라운드에 있지 않아도 됩니다.

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

자바

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

지연 설치 요청의 결과는 보장되지 않으며 진행 상황을 추적할 수 없습니다. 따라서 지연 설치를 지정한 모듈에 액세스하기 전에 먼저, 모듈이 설치되어 있는지 확인해야 합니다. 모듈을 즉시 사용할 수 있게 하려면 이전 섹션에 설명한 대로 대신 SplitInstallManager.startInstall()을 사용하여 설치를 요청하면 됩니다.

요청 상태 모니터링

진행률 표시줄을 업데이트하거나 설치 후 인텐트를 실행하거나 요청 오류를 적절하게 처리하려면 비동기 SplitInstallManager.startInstall() 작업에서 상태 업데이트를 수신 대기해야 합니다. 설치 요청의 업데이트 수신을 시작하려면 먼저, 아래와 같이 리스너를 등록하고 요청의 세션 ID를 가져와야 합니다.

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

자바

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

요청 오류 처리

모듈 다운로드 또는 설치를 실패한 경우 아래와 같이 addOnFailureListener()를 사용하여 적절하게 처리해야 합니다.

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

자바

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

아래 표에서는 앱이 처리해야 하는 오류 상태를 설명합니다.

오류 코드 설명 추천 작업
ACTIVE_SESSIONS_LIMIT_EXCEEDED 현재 다운로드 중인 기존 요청이 하나 이상 있으므로 요청이 거부되었습니다. 위 샘플과 같이 계속 다운로드 중인 요청이 있는지 확인합니다.
MODULE_UNAVAILABLE Google Play가 현재 설치된 버전의 앱, 기기 및 사용자 Google Play 계정을 기반으로 요청된 모듈을 찾을 수 없습니다. 사용자가 모듈에 액세스할 수 없는 경우 사용자에게 알립니다.
INVALID_REQUEST Google Play에서 요청을 수신했지만, 요청이 유효하지 않습니다. 요청에 포함된 정보가 완전하고 정확한지 확인합니다.
SESSION_NOT_FOUND 지정된 세션 ID의 세션을 찾을 수 없습니다. 세션 ID로 요청 상태를 모니터링하려면 세션 ID가 올바른지 확인합니다.
API_NOT_AVAILABLE Play Core 라이브러리는 현재 기기에서 지원되지 않습니다. 즉, 기기는 주문형 기능을 다운로드하고 설치할 수 없습니다. Android 4.4(API 수준 20) 이하를 실행하는 기기의 경우 dist:fusing 매니페스트 속성을 사용하여 설치 시 기능 모듈을 포함해야 합니다. 자세한 내용은 기능 모듈 매니페스트를 참고하세요.
ACCESS_DENIED 권한이 부족하여 앱에서 요청을 등록할 수 없습니다. 일반적으로 앱이 백그라운드에 있을 때 발생합니다. 앱이 포그라운드로 돌아올 때 요청을 시도합니다.
NETWORK_ERROR 네트워크 오류로 인해 요청이 실패했습니다. 사용자에게 네트워크 연결을 설정하거나 다른 네트워크로 변경하라는 메시지를 표시합니다.
INCOMPATIBLE_WITH_EXISTING_SESSION 이미 요청되었지만 아직 설치되지 않은 모듈이 하나 이상 요청에 포함되어 있습니다. 앱에서 이미 요청한 모듈을 포함하지 않는 새 요청을 만들거나 요청을 다시 시도하기 전에 현재 요청된 모든 모듈이 설치 완료될 때까지 기다립니다.

이미 설치된 모듈을 요청하는 것이 오류로 확인되지는 않습니다.

SERVICE_DIED 요청을 처리하는 서비스가 중단되었습니다. 요청을 다시 시도합니다.

SplitInstallStateUpdatedListener에서 이 오류 코드, FAILED 상태, 세션 ID -1과 함께 SplitInstallSessionState를 수신합니다.

INSUFFICIENT_STORAGE 기기에 기능 모듈을 설치할 수 있는 여유 공간이 없습니다. 사용자에게 저장용량이 부족하여 이 기능을 설치할 수 없다고 알립니다.
APP_NOT_OWNED Google Play에서 앱을 설치하지 않았으며 기능을 다운로드할 수 없습니다. Play Core 버전 1.9 이상에서는 이 오류가 지연 설치에서만 발생합니다. 사용자가 Google Play에서 앱을 받을 수 있게 하려면 startInstall()을 사용하여 필요한 사용자 확인(Play Core 버전 1.9 이상)을 얻으세요.

사용자가 주문형 모듈의 다운로드를 요청하고 오류가 발생하면 사용자에게 두 가지 옵션, 즉 다시 시도(요청을 다시 시도) 및 취소(요청을 취소)를 제공하는 대화상자를 표시하는 것을 고려합니다. 추가 지원을 위해 도움말 링크를 제공하여 Google Play 고객센터로 사용자를 안내할 수도 있습니다.

상태 업데이트 처리

리스너를 등록하고 요청의 세션 ID를 기록한 후 아래와 같이 StateUpdatedListener.onStateUpdate()를 사용하여 상태 변경사항을 처리합니다.

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
            }
        }
    }
}

자바

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
        }
    }
}

가능한 설치 요청 상태는 아래 표에 설명되어 있습니다.

요청 상태 설명 추천 작업
PENDING 요청이 수락되었으며 다운로드가 곧 시작됩니다. 사용자에게 다운로드에 관한 정보를 제공하기 위해 진행률 표시줄 같은 UI 구성요소를 초기화합니다.
REQUIRES_USER_CONFIRMATION 다운로드하려면 사용자 확인이 필요합니다. 이 상태는 앱이 Google Play를 통해 설치되지 않은 경우에 주로 나타납니다. 사용자에게 Google Play를 통해 기능을 다운로드했는지 확인하라는 메시지를 표시합니다. 자세한 내용은 사용자 확인을 받는 방법에 관한 섹션을 참고하세요.
DOWNLOADING 다운로드가 진행 중입니다. 다운로드 진행률 표시줄을 제공한다면 SplitInstallSessionState.bytesDownloaded()SplitInstallSessionState.totalBytesToDownload() 메서드를 사용하여 UI를 업데이트합니다(이 표 위에 있는 코드 샘플 참고).
다운로드됨 기기에서 모듈을 다운로드했지만 설치가 아직 시작되지 않았습니다. 앱에서 SplitCompat을 사용 설정하여 다운로드된 모듈에 액세스하고 이 상태가 표시되지 않도록 해야 합니다. 기능 모듈의 코드 및 리소스에 액세스하려면 이렇게 해야 합니다.
INSTALLING 현재 기기가 모듈을 설치 중입니다. 진행률 표시줄을 업데이트합니다. 일반적으로 이 상태는 짧습니다.
INSTALLED 모듈이 기기에 설치되었습니다. 모듈의 코드 및 리소스에 액세스하여 사용자가 계속 진행하도록 합니다.

모듈이 Android 8.0(API 수준 26) 이상에서 실행되는 Android 인스턴트 앱용 모듈이라면 splitInstallHelper를 사용하여 새 모듈로 앱 구성요소를 업데이트해야 합니다.

FAILED 모듈이 기기에 설치되기 전에 요청이 실패했습니다. 요청을 다시 시도하거나 취소하도록 사용자에게 메시지를 표시합니다.
CANCELING 기기가 요청을 취소하는 중입니다. 자세히 알아보려면 설치 요청 취소 방법에 관한 섹션을 참고하세요.
CANCELED 요청이 취소되었습니다.

사용자 확인 받기

일부의 경우 Google Play에서 다운로드 요청을 처리하기 전에 사용자 확인을 요구할 수 있습니다. 예를 들면 Google Play에서 앱을 설치하지 않은 경우가 있습니다. 이 경우 요청 상태는 REQUIRES_USER_CONFIRMATION을 보고하며 먼저 앱에서 사용자 확인을 받아야 기기가 요청된 모듈을 다운로드하고 설치할 수 있습니다. 확인을 받으려면 앱에서 사용자에게 다음과 같이 메시지를 표시해야 합니다.

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          /* activity = */ this,
          // You use this request code to later retrieve the user's decision.
          /* requestCode = */ MY_REQUEST_CODE)
    }
    ...
 }

자바

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          /* activity = */ this,
          // You use this request code to later retrieve the user's decision.
          /* requestCode = */ MY_REQUEST_CODE);
    }
    ...
 }

요청 상태는 사용자 응답에 따라 다음과 같이 업데이트됩니다.

  • 사용자가 확인을 수락하면 요청 상태가 PENDING으로 변경되고 다운로드가 진행됩니다.
  • 사용자가 확인을 거부하면 요청 상태가 CANCELED로 변경됩니다.
  • 대화상자가 사라지기 전에 사용자가 아무 선택도 하지 않으면 요청 상태는 REQUIRES_USER_CONFIRMATION으로 유지됩니다. 앱은 요청을 완료하기 위해 사용자에게 다시 메시지를 표시할 수 있습니다.

사용자의 응답으로 콜백을 수신하려면 아래와 같이 onActivityResult()를 사용합니다.

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  if (requestCode == MY_REQUEST_CODE) {
    // Handle the user's decision. For example, if the user selects "Cancel",
    // you may want to disable certain functionality that depends on the module.
  }
}

자바

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == MY_REQUEST_CODE) {
    // Handle the user's decision. For example, if the user selects "Cancel",
    // you may want to disable certain functionality that depends on the module.
  }
}

설치 요청 취소

모듈이 설치되기 전에 앱에서 요청을 취소해야 한다면 아래와 같이 요청의 세션 ID를 사용하여 cancelInstall() 메서드를 호출하면 됩니다.

Kotlin

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId)

자바

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId);

모듈 액세스

모듈 다운로드 이후 다운로드한 모듈의 코드 및 리소스에 액세스하려면 앱은 다운로드한 기능 모듈의 각 활동과 앱 모두에 대해 SplitCompat 라이브러리를 사용 설정해야 합니다.

그러나 플랫폼은 모듈을 다운로드한 후 일정 시간(일부 경우에는 며칠) 동안 모듈의 콘텐츠에 액세스하는 데 다음과 같은 제한사항이 적용됩니다.

  • 플랫폼은 모듈에서 도입한 새 매니페스트 항목을 적용할 수 없습니다.
  • 플랫폼은 알림과 같은 시스템 UI 구성요소의 모듈 리소스에 액세스할 수 없습니다. 이러한 리소스를 즉시 사용해야 한다면 리소스를 앱의 기본 모듈에 포함하는 것이 좋습니다.

SplitCompat 사용 설정

앱이 다운로드한 모듈의 코드 및 리소스에 액세스하려면 다음 섹션에서 설명하는 메서드 중 하나만 사용하여 SplitCompat을 사용 설정해야 합니다.

앱에 SplitCompat을 사용 설정한 이후에는 앱이 액세스하도록 할 기능 모듈의 각 활동에도 SplitCompat을 사용 설정해야 합니다.

매니페스트에 SplitCompatApplication 선언

SplitCompat을 사용 설정하는 가장 간단한 방법은 아래와 같이 앱 매니페스트에서 SplitCompatApplicationApplication 서브클래스로 선언하는 것입니다.

<application
    ...
    android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>

앱이 기기에 설치된 후에는 다운로드한 기능 모듈의 코드 및 리소스에 자동으로 액세스할 수 있습니다.

런타임 시 SplitCompat 호출

런타임 시 특정 활동 또는 서비스에서 SplitCompat을 사용 설정할 수도 있습니다. 기능 모듈에 포함된 활동을 실행하려면 이런 방법으로 SplitCompat을 사용 설정해야 합니다. 이렇게 하려면 아래와 같이 attachBaseContext를 재정의합니다.

맞춤 Application 클래스가 있다면 앱에서 SplitCompat을 사용 설정하기 위해 아래와 같이 SplitCompatApplication을 확장합니다.

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

자바

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication은 단순히 ContextWrapper.attachBaseContext()를 재정의하여 SplitCompat.install(Context applicationContext)을 포함합니다. Application 클래스에서 SplitCompatApplication을 확장하지 않도록 하려면 다음과 같이 직접 attachBaseContext()를 재정의할 수 있습니다.

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this)
}

자바

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this);
}

주문형 모듈이 인스턴트 앱 및 설치된 앱과 모두 호환된다면 다음과 같이 SplitCompat을 조건부로 호출할 수 있습니다.

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this)
    }
}

자바

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this);
    }
}

모듈 활동에 SplitCompat 사용 설정

기본 앱에 SplitCompat을 사용 설정한 이후에는 앱이 기능 모듈에서 다운로드하는 각 활동에 SplitCompat을 사용 설정해야 합니다. 이렇게 하려면 다음과 같이 SplitCompat.installActivity() 메서드를 사용합니다.

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this)
}

자바

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this);
}

설치된 모듈의 코드 및 리소스 액세스

기본 애플리케이션 컨텍스트와 기능 모듈의 활동에 SplitCompat을 사용 설정하기만 하면 주문형 모듈의 요청이 INSTALLED로 보고된 후, 모듈이 기본 APK의 일부인 것처럼 모듈의 코드 및 리소스를 사용할 수 있습니다.

앱에 설치된 다른 모듈에서 새로 설치된 모듈에 있는 애셋 또는 리소스에 액세스하려면 애플리케이션 컨텍스트를 사용하여 액세스해야 합니다. 리소스에 액세스하려는 구성요소의 컨텍스트는 아직 업데이트되지 않습니다. 또는 기능 모듈 설치 후에 구성요소를 다시 만들거나 구성요소에 SplitCompat을 설치할 수 있습니다.

또한 Android ApplicationInfo 객체, 콘텐츠 또는 이러한 객체를 포함하는 객체를 앱 내에 캐시하면 안 됩니다. 항상 앱 컨텍스트에서 필요에 따라 이러한 객체를 가져와야 합니다. 이러한 객체를 캐싱하면 최신 버전의 Android에 분할을 설치할 때 앱이 다운될 수 있습니다.

설치된 Android 인스턴트 앱 액세스

Android 인스턴트 앱 모듈이 INSTALLED로 보고된 후에는 새로고침한 앱 Context를 사용하여 모듈의 코드와 리소스에 액세스할 수 있습니다. 모듈을 설치하기 에 앱에서 만든 컨텍스트(예: 이미 변수에 저장된 컨텍스트)는 새 모듈의 콘텐츠를 포함하지 않습니다. 하지만 새로운 컨텍스트는 새 모듈의 콘텐츠를 포함하며, 예를 들어 createPackageContext를 사용하여 얻을 수 있습니다.

Kotlin

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                val newContext = context.createPackageContext(context.packageName, 0)
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                val am = newContext.assets
            }
        }
    }
}

자바

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                AssetManager am = newContext.getAssets();
        }
    }
}

Android 8.0 이상의 Android 인스턴트 앱

Android 8.0(API 수준 26) 이상에서 Android 인스턴트 앱의 주문형 모듈을 요청할 때 설치 요청이 INSTALLED로 보고된 후 SplitInstallHelper.updateAppInfo(Context context)를 호출하여 새 모듈의 컨텍스트로 앱을 업데이트해야 합니다. 그렇게 하지 않으면 앱이 모듈의 코드와 리소스를 인식할 수 없습니다. 앱의 메타데이터를 업데이트한 후에는 아래와 같이 새 Handler를 호출하여 다음 기본 스레드 이벤트 동안 모듈의 콘텐츠를 로드해야 합니다.

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

자바

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

C/C++ 라이브러리 로드

기기가 인스턴트 앱에 이미 다운로드한 모듈에서 C/C++ 라이브러리를 로드하려면 아래와 같이 SplitInstallHelper.loadLibrary(Context context, String libName)를 사용합니다.

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”)
                ...
            }
        }
    }
}

자바

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”);
                ...
        }
    }
}

설치된 모듈 관리

현재 기기에 설치된 기능 모듈을 확인하려면 아래와 같이 설치된 모듈 이름의 Set<String>을 반환하는 SplitInstallManager.getInstalledModules()를 호출하면 됩니다.

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

자바

Set<String> installedModules = splitInstallManager.getInstalledModules();

모듈 제거

아래와 같이 SplitInstallManager.deferredUninstall(List<String> moduleNames)을 호출하여 모듈을 제거하도록 기기에 요청할 수 있습니다.

Kotlin

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

자바

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));

모듈 제거는 즉시 실행되지 않습니다. 즉, 저장공간을 절약하기 위해 기기는 필요에 따라 백그라운드에서 모듈을 제거합니다. 기기에서 모듈을 삭제했는지 확인하려면 이전 섹션에 설명한 대로 SplitInstallManager.getInstalledModules()를 호출하여 그 결과를 검사하면 됩니다.

추가 언어 리소스 다운로드

App Bundle을 통해 기기는 앱을 실행하는 데 필요한 코드와 리소스만 다운로드합니다. 따라서 언어 리소스의 경우 사용자 기기는 기기의 설정에 현재 선택된 하나 이상의 언어와 일치하는 앱의 언어 리소스만 다운로드합니다.

앱에서 추가 언어 리소스에 액세스하도록 하려면(예: 인앱 언어 선택기 구현) Play Core 라이브러리를 사용하여 리소스를 주문형으로 다운로드하면 됩니다. 이 프로세스는 아래와 같이 기능 모듈을 다운로드하는 것과 비슷합니다.

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

자바

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

요청은 기능 모듈의 요청인 것처럼 처리됩니다. 즉, 평소와 같이 요청 상태를 모니터링할 수 있습니다.

앱에 추가 언어 리소스가 당장 필요하지 않다면 아래와 같이 앱이 백그라운드에 있는 동안 설치를 연기할 수 있습니다.

Kotlin

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

자바

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

다운로드한 언어 리소스 액세스

다운로드한 언어 리소스에 액세스하려면 앱은 아래와 같이 리소스에 액세스해야 하는 각 활동의 attachBaseContext() 메서드 내에서 SplitCompat.installActivity() 메서드를 실행해야 합니다.

Kotlin

override fun attachBaseContext(base: Context) {
  super.attachBaseContext(base)
  SplitCompat.installActivity(this)
}

자바

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  SplitCompat.installActivity(this);
}

앱에서 다운로드한 언어 리소스를 사용하려는 각 활동에 대해 기본 컨텍스트를 업데이트하고 Configuration을 통해 새 언어를 설정해야 합니다.

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

자바

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

이러한 변경사항을 적용하려면 새 언어가 설치되고 사용할 준비가 된 후에 활동을 다시 만들어야 합니다. Activity#recreate() 메서드를 사용하면 됩니다.

Kotlin

when (state.status()) {
  SplitInstallSessionStatus.INSTALLED -> {
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate()
  }
  ...
}

자바

switch (state.status()) {
  case SplitInstallSessionStatus.INSTALLED:
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate();
  ...
}

추가 언어 리소스 제거

기능 모듈과 마찬가지로 언제든지 추가 리소스를 제거할 수 있습니다. 제거를 요청하기 전에 다음과 같이 먼저 현재 설치된 언어를 확인해야 합니다.

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

자바

Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();

그런 다음, 아래와 같이 deferredLanguageUninstall() 메서드를 사용하여 제거할 언어를 결정하면 됩니다.

Kotlin

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

자바

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

참고 자료

Play Core 라이브러리 사용에 관해 자세히 알아보려면 다음 리소스를 참조하세요.

샘플

  • PlayCore API 샘플: PlayCore API를 사용하여 주문형 기능을 요청하고 다운로드하는 방법을 보여줍니다.
  • 동적 코드 로드 샘플: 설치된 기능 모듈에서 안전하게 코드에 액세스하는 세 가지 접근 방식을 보여줍니다.

Codelab

  • 주문형 모듈: 주문형 기능을 다운로드하여 설치하는 앱을 만드는 데 도움을 줍니다.

블로그 게시물

동영상