Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

Play Core 라이브러리로 모듈 다운로드

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

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

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

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

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

Play Core 라이브러리를 사용하려면 먼저 아래와 같이 라이브러리를 앱 모듈에 Gradle 종속성으로 가져와야 합니다.

    // In your app’s build.gradle file:
    ...
    dependencies {
        // This dependency is downloaded from the Google’s Maven repository.
        // So, make sure you also include that repository in your project's build.gradle file.
        implementation 'com.google.android.play:core:1.6.4'
        ...
    }
    

주문형 모듈 요청

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

예를 들어, 기기의 카메라를 사용하여 사진 메시지를 캡처하고 전송하는 주문형 모듈을 사용하는 앱이 있고 이 주문형 모듈은 manifest에 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이라는 manifest 속성을 사용하여 설치 시 동적 기능 모듈을 포함해야 합니다. 자세히 알아보려면 동적 기능 모듈 manifest를 읽어보세요.
ACCESS_DENIED 권한이 부족하여 앱에서 요청을 등록할 수 없습니다. 일반적으로 앱이 백그라운드에 있을 때 발생합니다. 앱이 포그라운드로 돌아올 때 요청을 시도합니다.
NETWORK_ERROR 네트워크 오류로 인해 요청이 실패했습니다. 사용자에게 네트워크 연결을 설정하거나 다른 네트워크로 변경하라는 메시지를 표시합니다.
INCOMPATIBLE_WITH_EXISTING_SESSION 이미 요청되었지만 아직 설치되지 않은 모듈이 하나 이상 요청에 포함되어 있습니다. 앱에서 이미 요청한 모듈을 포함하지 않는 새 요청을 만들거나 요청을 다시 시도하기 전에 현재 요청된 모든 모듈이 설치 완료될 때까지 기다립니다.

이미 설치된 모듈을 요청해도 오류가 해결되지 않습니다.

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

이 오류 코드는 상태 FAILED, 세션 ID -1로 설정되어 SplitInstallStateUpdatedListener에 업데이트로 노출됩니다.

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

상태 업데이트 처리

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

Kotlin

    override fun onStateUpdate(state : SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.FAILED
            && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
           // 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 다운로드하려면 사용자 확인이 필요합니다. 다운로드 크기가 10MB를 초과하기 때문일 수 있습니다. 사용자가 다운로드 요청을 수락하도록 메시지를 표시합니다. 자세한 내용은 사용자 확인을 받는 방법에 관한 섹션을 참조하세요.
DOWNLOADING 다운로드가 진행 중입니다 다운로드 진행률 표시줄을 제공한다면 UI를 업데이트하기 위해 SplitInstallSessionState.bytesDownloaded()SplitInstallSessionState.totalBytesToDownload() 메서드를 사용합니다(표 위에 있는 코드 샘플 참조).
DOWNLOADED 기기에서 모듈을 다운로드했지만 아직 설치가 시작되지 않았습니다. 앱은 SplitCompat를 사용 설정하여 다운로드된 모듈에 즉시 액세스하고 이 상태가 표시되지 않도록 해야 합니다. 그렇게 하지 않으면 다운로드는 INSTALLED로 전환되고 앱은 백그라운드에 진입한 후 어느 시점에만 코드와 리소스에 액세스합니다.
INSTALLING 현재 기기가 모듈을 설치 중입니다. 진행률 표시줄을 업데이트합니다. 일반적으로 이 상태는 짧습니다.
INSTALLED 모듈이 기기에 설치되었습니다. 사용자가 앱을 계속 사용하도록 모듈의 코드와 리소스에 액세스합니다.

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

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

사용자 확인 받기

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

Kotlin

    override fun onSessionStateUpdate(state: SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
            // Displays a dialog for the user to either “Download”
            // or “Cancel” 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 dialog for the user to either “Download”
            // or “Cancel” the request.
            splitInstallManager.startConfirmationDialogForResult(
              state,
              /* activity = */ this,
              // You use this request code to later retrieve the user's decision.
              /* requestCode = */ MY_REQUEST_CODE);
        }
        ...
     }
    

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

  • 사용자가 'Download'를 선택하면 요청 상태는 PENDING으로 변경되고 다운로드가 진행됩니다.
  • 사용자가 'Cancel'을 선택하면 요청 상태가 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 라이브러리를 사용 설정해야 합니다.

그러나 앱을 다시 시작하기 전에 플랫폼이 모듈의 콘텐츠에 액세스할 때 다음의 제한사항이 적용됩니다.

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

SplitCompat 사용 설정

앱이 다운로드한 모듈의 코드와 리소스에 즉시 액세스하려는 경우 아래 설명된 메서드 중 하나만 사용하여 SplitCompat를 사용 설정해야 합니다.

앱에서 SplitCompat를 사용 설정한 후에는 앱이 즉시 액세스할 동적 기능 모듈 내 각 활동의 SplitCompat도 사용 설정해야 합니다.

manifest에 SplitCompatApplication 선언

SplitCompat를 사용 설정하는 가장 간단한 방법은 아래와 같이 SplitCompatApplication을 앱 manifest의 Application 서브클래스로 선언하는 것입니다.

<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에 분할 APK를 설치할 때 앱이 다운될 수 있습니다.

설치된 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”);
                    ...
            }
        }
    }
    

설치된 모듈 관리

현재 기기에 설치된 동적 기능 모듈을 확인하려면 아래와 같이 SplitInstallManager.getInstalledModules()를 호출하면 되며 이 메서드는 설치된 모듈의 이름을 Set<String>으로 반환합니다.

Kotlin

    val installedModules: Set<String> = splitInstallManager.installedModules
    

자바

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

모듈 제거

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

Kotlin

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

자바

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

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

추가 언어 리소스 다운로드

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

앱에서 추가 언어 리소스에 액세스하도록 하려면(예: 인앱 언어 선택기 구현) 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를 사용하여 동적 기능을 요청하고 다운로드하는 방법을 보여줍니다.
  • 동적 코드 로드 샘플: 설치된 동적 기능 모듈의 코드에 안전하게 액세스하는 세 가지 접근 방식을 보여줍니다.

Codelabs

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

블로그 게시물

동영상