Konfigurowanie wyświetlania na żądanie

Moduły funkcji pozwalają oddzielić określone funkcje i zasoby od podstawowego modułu aplikacji i uwzględnić je w pakiecie aplikacji. Dzięki Play Feature Delivery użytkownicy mogą na przykład pobierać i instalować te komponenty na żądanie już po zainstalowaniu podstawowego pliku APK Twojej aplikacji.

Weźmy na przykład aplikację do obsługi SMS-ów, która ma funkcje zapisywania i wysyłania wiadomości graficznych, ale tylko niewielki odsetek użytkowników wysyła takie wiadomości. Warto dodać wiadomości w formie obrazów jako dostępny do pobrania moduł funkcji. Dzięki temu początkowe pobranie aplikacji będzie mniejsze dla wszystkich użytkowników i tylko ci, którzy wysyłają wiadomości, będą musieli pobrać ten dodatkowy komponent.

Pamiętaj, że taka modularyzacja wymaga większego wysiłku i prawdopodobnie refaktoryzacji istniejącego kodu aplikacji. Dlatego dobrze się zastanów, które funkcje aplikacji skorzystają najbardziej na udostępnieniu użytkownikom na żądanie. Aby dowiedzieć się więcej o optymalnych przypadkach użycia i wytycznych dotyczących funkcji na żądanie, przeczytaj artykuł Sprawdzone metody UX w zakresie wyświetlania na żądanie.

Jeśli z czasem chcesz stopniowo modułować funkcje aplikacji bez włączania zaawansowanych opcji dostarczania, takich jak dostarczanie na żądanie, skonfiguruj dostarczanie w czasie instalacji.

Na tej stronie dowiesz się, jak dodać moduł funkcji do projektu aplikacji i skonfigurować go pod kątem dostarczania na żądanie. Zanim zaczniesz, upewnij się, że używasz Android Studio 3.5 lub nowszego i wtyczki Androida do obsługi Gradle w wersji 3.5.0 lub nowszej.

Konfigurowanie nowego modułu pod kątem wyświetlania na żądanie

Najłatwiejszym sposobem utworzenia nowego modułu funkcji jest użycie Android Studio 3.5 lub nowszego. Moduły funkcji są nieodłącznie zależne od podstawowego modułu aplikacji, więc możesz je dodać tylko do istniejących projektów aplikacji.

Aby dodać moduł funkcji do projektu aplikacji za pomocą Android Studio, wykonaj te czynności:

  1. Otwórz projekt aplikacji w IDE.
  2. Na pasku menu wybierz File > New > New Module (Plik > Nowy > Nowy moduł).
  3. W oknie Create New Module (Utwórz nowy moduł) wybierz Dynamic Feature Module (Moduł funkcji dynamicznych) i kliknij Next (Dalej).
  4. W sekcji Skonfiguruj nowy moduł wykonaj te czynności:
    1. W menu wybierz Podstawowy moduł aplikacji dla projektu aplikacji.
    2. Wypełnij pole Nazwa modułu. IDE korzysta z tej nazwy, aby zidentyfikować moduł jako podprojekt Gradle w pliku ustawień Gradle. Gdy tworzysz pakiet aplikacji, Gradle używa ostatniego elementu nazwy podprojektu, aby wstawić atrybut <manifest split> w pliku manifestu modułu funkcji.
    3. Podaj nazwę pakietu modułu. Domyślnie Android Studio sugeruje nazwę pakietu, która łączy nazwę pakietu głównego modułu podstawowego i nazwę modułu określoną w poprzednim kroku.
    4. Wybierz Minimalny poziom interfejsu API, który ma być obsługiwany przez moduł. Ta wartość powinna być zgodna z wartością modułu podstawowego.
  5. Kliknij Dalej.
  6. W sekcji Opcje pobierania modułu wykonaj te czynności:

    1. Określ Tytuł modułu za pomocą maksymalnie 50 znaków. Platforma używa tego tytułu, aby zidentyfikować moduł dla użytkowników na przykład przy potwierdzaniu, czy użytkownik chce go pobrać. Z tego powodu podstawowy moduł aplikacji musi zawierać tytuł modułu jako zasób typu ciąg znaków, który możesz przetłumaczyć. Podczas tworzenia modułu za pomocą Android Studio IDE dodaje zasób ciągu znaków do modułu podstawowego i wstrzykuje ten wpis w pliku manifestu modułu funkcji:

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. W menu w sekcji Uwzględnianie podczas instalacji wybierz Nie uwzględniaj modułu podczas instalacji. Android Studio wstawia w pliku manifestu modułu ten fragment, aby odzwierciedlić Twój wybór:

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. Zaznacz pole obok opcji Fusing, jeśli chcesz, aby ten moduł był dostępny na urządzeniach z Androidem 4.4 (poziom interfejsu API 20) lub starszym i znalazły się w wielu plikach APK. Oznacza to, że możesz włączyć działanie na żądanie w tym module i wyłączyć funkcję fusing, aby pomijać ją na urządzeniach, które nie obsługują pobierania i instalowania dzielonych pakietów APK. Android Studio wstawia w pliku manifestu modułu ten fragment, aby odzwierciedlić Twój wybór:

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. Kliknij Zakończ.

Gdy Android Studio zakończy tworzenie modułu, sprawdź jego zawartość w panelu Projekt (na pasku menu wybierz Widok > Okna narzędziowe > Projekt). Domyślny kod, zasoby i organizacja powinny być podobne do tych w standardowym module aplikacji.

Następnie trzeba wdrożyć funkcję instalacji na żądanie przy użyciu biblioteki Play Feature Delivery.

Dołącz do projektu bibliotekę Play Feature Delivery

Zanim zaczniesz, musisz dodać do projektu bibliotekę Play Feature Delivery.

Poproś o moduł na żądanie

Gdy aplikacja musi użyć modułu funkcji, może go zażądać, gdy znajduje się na pierwszym planie, za pomocą klasy SplitInstallManager. Podczas wysyłania żądania aplikacja musi określić nazwę modułu zgodnie z definicją zawartą w elemencie split w pliku manifestu modułu docelowego. Gdy tworzysz moduł funkcji w Android Studio, system kompilacji używa podanej przez Ciebie nazwy modułu, aby wstawić tę właściwość do pliku manifestu modułu podczas kompilacji. Więcej informacji znajdziesz w plikach manifestu modułu funkcji.

Weźmy na przykład aplikację z modułem na żądanie służącym do zapisywania i wysyłania wiadomości z obrazem za pomocą aparatu urządzenia. Ten moduł na żądanie zawiera w swoim manifeście element split="pictureMessages". Poniższy przykład wykorzystuje parametr SplitInstallManager, aby zażądać modułu pictureMessages (wraz z dodatkowym modułem na niektóre filtry promocyjne):

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 ->  ... }

Java

// 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 -> { ... });

Gdy aplikacja prosi o moduł na żądanie, biblioteka funkcji Google Play stosuje strategię „wypal się i zapomnij”. Oznacza to, że wysyła żądanie pobrania modułu na platformę, ale nie sprawdza, czy instalacja się powiodła. Aby móc kontynuować ścieżkę użytkownika po instalacji lub w sposób sprawny obsługiwać błędy, monitoruj stan żądania.

Uwaga: możesz poprosić o moduł funkcji, który jest już zainstalowany na urządzeniu. Gdy wykryje, że moduł jest już zainstalowany, API od razu uznaje żądanie za zrealizowane. Dodatkowo po zainstalowaniu modułu Google Play aktualizuje go automatycznie. Oznacza to, że gdy prześlesz nową wersję pakietu aplikacji, platforma zaktualizuje wszystkie zainstalowane pliki APK, które należą do Twojej aplikacji. Więcej informacji znajdziesz w artykule Zarządzanie aktualizacjami aplikacji.

Aby aplikacja miała dostęp do kodu i zasobów modułu, musisz włączyć w aplikacji SplitCompat. Pamiętaj, że w przypadku aplikacji błyskawicznych na Androida aplikacja SplitCompat nie jest wymagana.

Odrocz instalację modułów na żądanie

Jeśli nie potrzebujesz aplikacji do natychmiastowego pobierania i instalowania modułu na żądanie, możesz odroczyć instalację, gdy aplikacja działa w tle. np. gdy chcesz wstępnie wczytać materiały promocyjne do późniejszego wprowadzenia aplikacji na rynek.

Możesz określić moduł do pobrania później, używając metody deferredInstall(), jak pokazano poniżej. Ponadto w przeciwieństwie do SplitInstallManager.startInstall() aplikacja nie musi być na pierwszym planie, aby można było wysłać żądanie odroczonej instalacji.

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"))

Java

// 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"));

Prośby o odroczone instalacje przebiegają najlepiej i nie można śledzić ich postępu. Zanim więc spróbujesz uzyskać dostęp do modułu, który jest przeznaczony do odroczonej instalacji, sprawdź, czy został on zainstalowany. Jeśli chcesz, aby moduł był dostępny od razu, użyj polecenia SplitInstallManager.startInstall(), aby go wysłać (jak pokazano w poprzedniej sekcji).

Monitorowanie stanu żądania

Aby móc zaktualizować pasek postępu, uruchamiać intencję po instalacji lub w sposób sprawny obsługiwać błąd żądania, musisz nasłuchiwać aktualizacji stanu z asynchronicznego zadania SplitInstallManager.startInstall(). Zanim zaczniesz otrzymywać aktualizacje żądania instalacji, zarejestruj odbiornik i uzyskaj identyfikator sesji dla żądania, jak pokazano poniżej.

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)

Java

// 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);

Obsługa błędów żądań

Pamiętaj, że instalacja modułów funkcji na żądanie może czasami się nie powieść, tak jak instalacja aplikacji nie zawsze się kończy. Instalacja może się nie powieść z powodu braku miejsca na urządzeniu, braku połączenia z siecią lub braku zalogowania się w Sklepie Google Play. Aby dowiedzieć się, jak postępować w takich sytuacjach z perspektywy użytkownika, zapoznaj się z naszymi wskazówkami dotyczącymi UX dla wyświetlania na żądanie.

W kontekście kodu należy radzić sobie z błędami pobierania lub instalowania modułu za pomocą interfejsu addOnFailureListener(), jak pokazano poniżej:

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.
                    }
                }
            }
        }
}

Java

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.
                    }
                }
            }
        });
}

W tabeli poniżej znajdziesz stany błędów, które mogą być konieczne przez aplikację:

Kod błędu Opis Sugerowane działanie
AKTYWNE_SESSIONS_LIMIT_PRZEKROCZONO Prośba została odrzucona, ponieważ co najmniej 1 żądanie jest obecnie pobierane. Sprawdź, czy są jakieś żądania, które są nadal pobierane, tak jak w przykładzie powyżej.
MODULE_NIEDOSTĘPNY Google Play nie może znaleźć żądanego modułu na podstawie obecnie zainstalowanej wersji aplikacji, urządzenia lub konta Google Play użytkownika. Jeśli użytkownik nie ma dostępu do modułu, powiadom go o tym.
NIEPRAWIDŁOWE_ŻĄDANIE Google Play otrzymało żądanie, ale jest ono nieprawidłowe. Sprawdź, czy informacje zawarte w prośbie są kompletne i dokładne.
SESJA_NIE ZNALEZIONO Nie znaleziono sesji o podanym identyfikatorze. Jeśli próbujesz monitorować stan żądania według identyfikatora sesji, upewnij się, że identyfikator sesji jest prawidłowy.
API_NOT_AVAILABLE (niedostępny) Biblioteka Play Feature Delivery nie jest obsługiwana na obecnym urządzeniu. Oznacza to, że nie można pobierać ani instalować funkcji na żądanie. W przypadku urządzeń z Androidem 4.4 (poziom interfejsu API 20) lub starszym należy uwzględnić moduły funkcji podczas instalacji za pomocą właściwości pliku manifestu dist:fusing. Więcej informacji znajdziesz w pliku manifestu modułu funkcji.
BŁĄD_SIECI Żądanie nie zostało zrealizowane z powodu błędu sieci. Poproś użytkownika, aby nawiązał połączenie sieciowe lub przełączył się na inną sieć.
ODRZUCONO Aplikacja nie może zarejestrować żądania z powodu niewystarczających uprawnień. Zwykle dzieje się tak, gdy aplikacja działa w tle. Ponów próbę, gdy aplikacja wróci na pierwszy plan.
NIEZGODNE_Z_ISTNIEJĄCĄ_SESJĄ Żądanie zawiera co najmniej 1 moduł, który został już zażądany, ale nie został jeszcze zainstalowany. Utwórz nowe żądanie, które nie zawiera modułów, o które prosiła już Twoja aplikacja, lub poczekaj na zakończenie instalacji wszystkich aktualnie żądanych modułów, zanim spróbujesz ponownie.

Pamiętaj, że żądanie pobrania modułu, który został już zainstalowany, nie powoduje błędu.

WYKONANY_USŁUGA Usługa odpowiedzialna za obsługę tego żądania nie działa. Spróbuj ponownie.

SplitInstallStateUpdatedListener otrzymuje SplitInstallSessionState z tym kodem błędu, stanem FAILED i identyfikatorem sesji -1.

ZA MAŁO_MIEJSCA Na urządzeniu nie ma wystarczającej ilości wolnego miejsca, aby zainstalować moduł funkcji. Powiadom użytkownika, że nie ma wystarczającej ilości miejsca do zainstalowania tej funkcji.
SPLITCOMPAT_VERIFICATION_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_CONTENT_ERROR, SplitCompat nie może wczytać modułu funkcji. Te błędy powinny zniknąć automatycznie po ponownym uruchomieniu aplikacji.
NIE ZNALEZIONO SKLEPU PLAY Aplikacja Sklep Play nie jest zainstalowana na urządzeniu. Poinformuj użytkownika, że do pobrania tej funkcji wymagana jest aplikacja Sklep Play.
APLIKACJA_NIE_DOSTĘPNA Ta aplikacja nie została zainstalowana z Google Play i nie można pobrać tej funkcji. Ten błąd może występować tylko w przypadku odroczonych instalacji. Jeśli chcesz, aby użytkownik pozyskał aplikację z Google Play, użyj narzędzia startInstall(), które może uzyskać niezbędne potwierdzenie przez użytkownika.
BŁĄD_WEWNĘTRZNY W Sklepie Play wystąpił błąd wewnętrzny. Spróbuj ponownie.

Jeśli użytkownik poprosi o pobranie modułu na żądanie i wystąpi błąd, rozważ wyświetlenie mu okna z 2 opcjami: Spróbuj ponownie (co spowoduje ponowne przesłanie żądania) lub Anuluj (co spowoduje rezygnację z żądania). Aby uzyskać dodatkową pomoc, musisz też podać link do Pomocy, który przekieruje użytkowników do Centrum pomocy Google Play.

Aktualizacje stanu uchwytu

Po zarejestrowaniu odbiornika i zarejestrowaniu identyfikatora sesji dla żądania użyj narzędzia StateUpdatedListener.onStateUpdate() do obsługi zmian stanu, jak pokazano poniżej.

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.
            }
        }
    }
}

Java

@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.
        }
    }
}

Możliwe stany prośby o instalację zostały opisane w tabeli poniżej.

Stan żądania Opis Sugerowane działanie
OCZEKUJĄCA Prośba została zaakceptowana i wkrótce powinno się rozpocząć pobieranie. Zainicjuj komponenty interfejsu, takie jak pasek postępu, aby przekazać użytkownikom opinię o pobieraniu.
REQUIRES_POTWIERDZENIA_UŻYTKOWNIKA Pobranie wymaga potwierdzenia przez użytkownika. Najczęściej ten stan występuje wtedy, gdy aplikacja nie została zainstalowana z Google Play. Wyświetlaj użytkownikowi prośbę o potwierdzenie pobrania funkcji w Google Play. Więcej informacji znajdziesz w sekcji o uzyskiwaniu potwierdzenia użytkownika.
POBIERAM Trwa pobieranie. Jeśli udostępniasz pasek postępu pobierania, zaktualizuj interfejs za pomocą metod SplitInstallSessionState.bytesDownloaded() i SplitInstallSessionState.totalBytesToDownload() (zobacz przykładowy kod nad tabelą).
POBRANE Moduł został pobrany na urządzenie, ale instalacja jeszcze się nie rozpoczęła. Aby aplikacje miały dostęp do pobranych modułów i nie wyświetlały się w tym stanie, włącz SplitCompat. Jest to wymagane, aby mieć dostęp do kodu i zasobów modułu funkcji.
INSTALUJĘ Urządzenie instaluje moduł. Zaktualizuj pasek postępu. Ten stan jest zwykle krótki.
ZAINSTALOWANO Moduł jest zainstalowany na urządzeniu. Kod dostępu i zasób w module, dzięki któremu użytkownik może kontynuować podróż.

Jeśli moduł dotyczy aplikacji błyskawicznej na Androida uruchomionej na Androidzie 8.0 (poziom interfejsu API 26) lub nowszym, musisz użyć splitInstallHelper, by zaktualizować komponenty aplikacji za pomocą nowego modułu.

PORAŻKA Żądanie nie powiodło się przed zainstalowaniem modułu na urządzeniu. Poproś użytkownika o ponowne przesłanie prośby lub jej anulowanie.
ANULuję Trwa anulowanie żądania na urządzeniu. Więcej informacji znajdziesz w sekcji o anulowaniu prośby o instalację.
ODWOŁANY Prośba została anulowana.

Uzyskiwanie potwierdzenia użytkownika

W niektórych przypadkach przed spełnieniem żądania pobrania Google Play może wymagać potwierdzenia przez użytkownika. Dzieje się tak na przykład wtedy, gdy aplikacja nie została zainstalowana przez Google Play lub jeśli próbujesz pobrać dużą ilość danych przez mobilną transmisję danych. W takich przypadkach stan żądania zgłasza REQUIRES_USER_CONFIRMATION, a aplikacja musi uzyskać potwierdzenie użytkownika, zanim urządzenie będzie mogło pobrać i zainstalować moduły w żądaniu. Aby uzyskać potwierdzenie, aplikacja powinna zapytać użytkownika w ten sposób:

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,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher)
    }
    ...
 }

Java

@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,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher);
    }
    ...
 }

Możesz zarejestrować program uruchamiający wyniki aktywności, korzystając z wbudowanej umowy ActivityResultContracts.StartIntentSenderForResult. Zapoznaj się z artykułem Interfejsy API wyników działań.

Stan żądania jest aktualizowany w zależności od odpowiedzi użytkownika:

  • Jeśli użytkownik zaakceptuje potwierdzenie, stan prośby zmieni się na PENDING i pobieranie będzie kontynuowane.
  • Jeśli użytkownik odmówi potwierdzenia, stan prośby zmieni się na CANCELED.
  • Jeśli użytkownik nie dokona wyboru przed zniszczeniem okna, stan żądania pozostanie na poziomie REQUIRES_USER_CONFIRMATION. Aplikacja może ponownie poprosić użytkownika o wykonanie tego żądania.

Aby otrzymać wywołanie zwrotne z odpowiedzią użytkownika, możesz zastąpić działanieActivityResultCallback zgodnie z poniższym opisem.

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // Handle the user's decision. For example, if the user selects "Cancel",
            // you may want to disable certain functionality that depends on the module.
        }
    });

Anulowanie prośby o instalację

Jeśli Twoja aplikacja musi anulować żądanie przed zainstalowaniem, może wywołać metodę cancelInstall(), używając identyfikatora sesji żądania, jak pokazano poniżej.

Kotlin

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

Java

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

Dostęp do modułów

Aby po pobraniu uzyskać dostęp do kodu i zasobów z pobranego modułu, aplikacja musi włączyć bibliotekę SpitCompat zarówno dla aplikacji, jak i dla każdego działania w pobranych przez nią modułach funkcji.

Pamiętaj jednak, że przez pewien czas (w niektórych przypadkach) od pobrania modułu na platformie obowiązują te ograniczenia:

  • Platforma nie może stosować żadnych nowych wpisów manifestu wprowadzonych przez moduł.
  • Platforma nie ma dostępu do zasobów modułu związanych z komponentami interfejsu systemowego, takimi jak powiadomienia. Jeśli musisz natychmiast skorzystać z tych zasobów, uwzględnij je w module podstawowym aplikacji.

Włącz SplitCompat

Aby aplikacja miała dostęp do kodu i zasobów z pobranego modułu, musisz włączyć SplitCompat, używając tylko jednej z metod opisanych w poniższych sekcjach.

Po włączeniu SplitCompat w swojej aplikacji musisz też włączyć tę funkcję dla każdej aktywności w modułach funkcji, do których Twoja aplikacja ma mieć dostęp.

Zadeklaruj SplitCompatApplication w pliku manifestu

Najprostszym sposobem włączenia SplitCompat jest zadeklarowanie SplitCompatApplication jako podklasy Application w pliku manifestu aplikacji, jak w tym przykładzie:

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

Po zainstalowaniu aplikacji na urządzeniu automatycznie uzyskasz dostęp do kodu i zasobów z pobranych modułów funkcji.

Wywołuj SplitCompat w czasie działania

Możesz też włączyć SplitCompat w przypadku określonych działań lub usług w czasie działania. Ten sposób włączenia SplitCompat jest wymagany do uruchamiania działań zawartych w modułach funkcji. Aby to zrobić, zastąp attachBaseContext w sposób pokazany poniżej.

Jeśli masz niestandardową klasę Application, użyj jej zamiast rozszerzenia SplitCompatApplication, aby włączyć SplitCompat dla swojej aplikacji, jak pokazano poniżej:

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication zastępuje tylko ContextWrapper.attachBaseContext(), aby uwzględnić SplitCompat.install(Context applicationContext). Jeśli nie chcesz, aby klasa Application rozszerzyła SplitCompatApplication, możesz ręcznie zastąpić metodę attachBaseContext() w ten sposób:

Kotlin

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

Java

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

Jeśli Twój moduł na żądanie jest zgodny zarówno z aplikacjami błyskawicznymi, jak i aplikacjami zainstalowanymi, możesz wywołać funkcję SplitCompat warunkowo w ten sposób:

Kotlin

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

Java

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

Włącz SplitCompat do działań w module

Gdy włączysz SplitCompat dla aplikacji podstawowej, musisz ją włączyć dla każdej aktywności pobieranej przez Twoją aplikację w module funkcji. Aby to zrobić, użyj metody SplitCompat.installActivity() w ten sposób:

Kotlin

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

Java

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

Uzyskiwanie dostępu do komponentów zdefiniowanych w modułach funkcji

Rozpocznij ćwiczenie zdefiniowane w module funkcji

Po włączeniu usługi SplitCompat możesz uruchamiać działania zdefiniowane w modułach funkcji za pomocą narzędzia startActivity().

Kotlin

startActivity(Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...))

Java

startActivity(new Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...));

Pierwszym parametrem setClassName jest nazwa pakietu aplikacji, a drugi to pełna nazwa klasy aktywności.

Jeśli w module funkcji pobranym na żądanie znajduje się jakaś aktywność, musisz w niej włączyć w niej narzędzie SplitCompat.

Uruchom usługę zdefiniowaną w module funkcji

Po włączeniu usługi SplitCompat możesz uruchamiać usługi zdefiniowane w modułach funkcji za pomocą narzędzia startService().

Kotlin

startService(Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...))

Java

startService(new Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...));

Eksportowanie komponentu zdefiniowanego w module funkcji

Wyeksportowanych komponentów Androida nie należy umieszczać w modułach opcjonalnych.

System kompilacji scala wpisy manifestu wszystkich modułów z modułem podstawowym. Jeśli moduł opcjonalny zawierał wyeksportowany komponent, będzie on dostępny nawet przed zainstalowaniem modułu i może spowodować awarię z powodu brakującego kodu przy wywołaniu z innej aplikacji.

Nie stanowi to problemu w przypadku komponentów wewnętrznych. Dostęp do nich ma tylko aplikacja, więc przed uzyskaniem dostępu do komponentu aplikacja może sprawdzić, czy moduł jest zainstalowany.

Jeśli potrzebujesz wyeksportowanego komponentu i chcesz, aby jego zawartość znajdowała się w module opcjonalnym, rozważ wdrożenie wzorca serwera proxy. Aby to zrobić, dodaj do podstawy wyeksportowanego przez serwer proxy komponent serwera proxy. Po uzyskaniu dostępu ten komponent serwera proxy może sprawdzać, czy nie ma modułu zawierającego treść. Jeśli moduł jest obecny, komponent serwera proxy może uruchomić wewnętrzny komponent z modułu za pomocą interfejsu Intent, przekazując intencję z aplikacji wywołującej. Gdy moduł nie jest obecny, komponent może go pobrać lub zwrócić odpowiedni komunikat o błędzie do aplikacji wywołującej.

Dostęp do kodu i zasobów z zainstalowanych modułów

Jeśli włączysz SplitCompat na potrzeby podstawowego kontekstu aplikacji i działań w module funkcji, po zainstalowaniu modułu funkcji możesz używać kodu i zasobów z modułu funkcji tak, jakby stanowił on część podstawowego pliku APK.

Kod dostępu z innego modułu

Uzyskiwanie dostępu do kodu podstawowego z modułu

Kod umieszczony wewnątrz modułu podstawowego może być używany bezpośrednio przez inne moduły. Nie musisz robić nic szczególnego – po prostu zaimportuj i użyj odpowiednich zajęć.

Uzyskiwanie dostępu do kodu modułu z innego modułu

Obiekt lub klasa wewnątrz modułu nie może być statycznie dostępna z innego modułu bezpośrednio, ale można uzyskać do nich dostęp pośrednio za pomocą odbicia.

Zwracaj uwagę na to, jak często się to zdarza, ponieważ koszty działania są związane z odczuciem. W złożonych przypadkach używaj platform wstrzykiwania zależności, takich jak Dagger 2, aby zagwarantować pojedyncze wywołanie odczucia w czasie życia aplikacji.

Aby uprościć interakcje z obiektem po utworzeniu instancji, zalecamy zdefiniowanie interfejsu w module podstawowym i jego implementację w module funkcji. Przykłady:

Kotlin

// In the base module
interface MyInterface {
  fun hello(): String
}

// In the feature module
object MyInterfaceImpl : MyInterface {
  override fun hello() = "Hello"
}

// In the base module, where we want to access the feature module code
val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl")
    .kotlin.objectInstance as MyInterface).hello();

Java

// In the base module
public interface MyInterface {
  String hello();
}

// In the feature module
public class MyInterfaceImpl implements MyInterface {
  @Override
  public String hello() {
    return "Hello";
  }
}

// In the base module, where we want to access the feature module code
String stringFromModule =
   ((MyInterface) Class.forName("com.package.module.MyInterfaceImpl").getConstructor().newInstance()).hello();

Dostęp do zasobów z innego modułu

Po zainstalowaniu modułu możesz uzyskiwać dostęp do jego zasobów w standardowy sposób, mając na uwadze 2 zastrzeżenia:

  • Gdy uzyskujesz dostęp do zasobu z innego modułu, moduł nie ma dostępu do jego identyfikatora, ale dostęp do zasobu można nadal uzyskać przy użyciu nazwy. Pamiętaj, że pakiet, za pomocą którego odwołuje się do zasobu, to pakiet modułu, w którym ten zasób jest zdefiniowany.
  • Jeśli chcesz uzyskać dostęp do zasobów, które znajdują się w nowo zainstalowanym module z poziomu innego zainstalowanego modułu aplikacji, musisz to zrobić w kontekście aplikacji. Kontekst komponentu, który próbuje uzyskać dostęp do zasobów, nie zostanie jeszcze zaktualizowany. Możesz też ponownie utworzyć ten komponent (np. wywołając metodę Activity.recreate()) lub ponownie zainstalować na nim SplitCompat po zainstalowaniu modułu funkcji.

Wczytywanie kodu natywnego w aplikacji przy użyciu dostarczania na żądanie

Jeśli korzystasz z modułów funkcji na żądanie, do wczytywania wszystkich bibliotek natywnych zalecamy używanie narzędzia ReLinker. ReLinker rozwiązuje problem z wczytywaniem bibliotek natywnych po zainstalowaniu modułu funkcji. Więcej informacji o ReLinker znajdziesz w artykule Android JNI – wskazówki.

Wczytywanie kodu natywnego z modułu opcjonalnego

Po zainstalowaniu podziału zalecamy wczytanie natywnego kodu za pomocą narzędzia ReLinker. W przypadku aplikacji błyskawicznych użyj tej specjalnej metody.

Jeśli do wczytywania kodu natywnego używasz narzędzia System.loadLibrary(), a biblioteka natywna zależy od innej biblioteki w module, musisz najpierw wczytać tę bibliotekę ręcznie. Jeśli używasz narzędzia ReLinker, jego odpowiednikiem jest Relinker.recursively().loadLibrary().

Jeśli używasz biblioteki dlopen() w kodzie natywnym do wczytywania biblioteki zdefiniowanej w module opcjonalnym, nie będzie ona działać ze ścieżkami biblioteki względnej. Najlepszym rozwiązaniem jest pobranie ścieżki bezwzględnej biblioteki z kodu Java za pomocą interfejsu ClassLoader.findLibrary() i użycie jej w wywołaniu dlopen(). Zrób to przed wpisaniem kodu natywnego lub użyj wywołania JNI z kodu natywnego w Javie.

Dostęp do zainstalowanych aplikacji błyskawicznych na Androida

Gdy moduł aplikacji błyskawicznej na Androida zostanie zgłoszony jako INSTALLED, możesz uzyskać dostęp do jego kodu i zasobów za pomocą odświeżonego kontekstu aplikacji. Kontekst utworzony przez aplikację przed zainstalowaniem modułu (np. zapisany w zmiennej) nie zawiera zawartości nowego modułu. Jest to jednak możliwe w przypadku świeżego kontekstu – można to uzyskać na przykład przy użyciu 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
            }
        }
    }
}

Java

// 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();
        }
    }
}

Aplikacje błyskawiczne na Androida na Androida 8.0 lub nowszego

Jeśli wysyłasz żądanie modułu na żądanie w przypadku aplikacji błyskawicznej na Androida w wersji na Androida 8.0 (poziom interfejsu API 26) lub nowszego, po zgłoszeniu żądania instalacji jako INSTALLED musisz zaktualizować aplikację, podając kontekst nowego modułu, używając wywołania SplitInstallHelper.updateAppInfo(Context context). W przeciwnym razie aplikacja nie zna jeszcze kodu i zasobów modułu. Po zaktualizowaniu metadanych aplikacji należy wczytać zawartość modułu podczas następnego zdarzenia wątku głównego, wywołując nowy obiekt Handler, jak pokazano poniżej:

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
                        ...
                    }
                }
            }
        }
    }
}

Java

@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();
                        ...
                    }
                });
            }
        }
    }
}

Wczytaj biblioteki C/C++

Jeśli chcesz wczytać biblioteki C/C++ z modułu pobranego już przez urządzenie w aplikacji błyskawicznej, użyj SplitInstallHelper.loadLibrary(Context context, String libName) w ten sposób:

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”)
                ...
            }
        }
    }
}

Java

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

Znane ograniczenia

  • Nie można używać komponentu WebView Androida w działaniach, które uzyskują dostęp do zasobów lub zasobów z modułu opcjonalnego. Wynika to z niezgodności między komponentem WebView i SpllitCompat w przypadku interfejsu Android API na poziomie 28 lub niższym.
  • Obiektów Androida ApplicationInfo, ich zawartości ani obiektów, które je zawierają, nie możesz zapisywać w pamięci podręcznej w swojej aplikacji. Zawsze należy pobierać te obiekty z kontekstu aplikacji. Buforowanie takich obiektów może spowodować awarię aplikacji podczas instalowania modułu funkcji.

Zarządzaj zainstalowanymi modułami

Aby sprawdzić, które moduły funkcji są obecnie zainstalowane na urządzeniu, możesz wywołać metodę SplitInstallManager.getInstalledModules(), która zwróci Set<String> nazw zainstalowanych modułów, jak pokazano poniżej.

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

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

Odinstaluj moduły

Możesz poprosić urządzenie o odinstalowanie modułów, wywołując SplitInstallManager.deferredUninstall(List<String> moduleNames) w sposób pokazany poniżej.

Kotlin

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

Java

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

Odinstalowania modułu nie następują natychmiast. Oznacza to, że w razie potrzeby urządzenie odinstalowuje je w tle, by zaoszczędzić miejsce. Aby upewnić się, że urządzenie usunęło moduł, wywołaj metodę SplitInstallManager.getInstalledModules() i sprawdź wynik w sposób opisany w poprzedniej sekcji.

Pobierz dodatkowe materiały dotyczące języków

Dzięki pakietom aplikacji urządzenia pobierają tylko kod i zasoby wymagane do uruchomienia aplikacji. Oznacza to, że w przypadku zasobów językowych urządzenie pobiera tylko te zasoby językowe aplikacji, które odpowiadają co najmniej jednemu językowi aktualnie wybranemu w ustawieniach urządzenia.

Jeśli chcesz, aby aplikacja miała dostęp do dodatkowych zasobów językowych (np. w celu zaimplementowania selektora języka w aplikacji), możesz pobrać te funkcje na żądanie z biblioteki Play Feature Delivery. Proces ten jest podobny do pobierania modułu funkcji, jak pokazano poniżej.

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)

Java

// 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);

Żądanie jest obsługiwane tak, jakby było żądaniem modułu funkcji. Oznacza to, że możesz monitorować stan żądania w zwykły sposób.

Jeśli aplikacja nie wymaga natychmiast dodatkowych zasobów językowych, możesz odroczyć instalację, gdy działa w tle, jak pokazano poniżej.

Kotlin

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

Java

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

Dostęp do pobranych zasobów językowych

Aby uzyskać dostęp do pobranych zasobów językowych, aplikacja musi uruchomić metodę SplitCompat.installActivity() w ramach metody attachBaseContext() w ramach każdej aktywności, która wymaga dostępu do tych zasobów, jak pokazano poniżej.

Kotlin

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

Java

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

W przypadku każdej aktywności, w której chcesz używać zasobów językowych pobranych przez aplikację, zaktualizuj kontekst podstawowy i ustaw nowy język za pomocą 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)
}

Java

@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);
}

Aby zmiany zaczęły obowiązywać, musisz odtworzyć swoją aktywność po zainstalowaniu nowego języka i gotowości do użycia. Możesz użyć metody Activity#recreate().

Kotlin

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

Java

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

Odinstaluj dodatkowe zasoby językowe

Podobnie jak moduły funkcji, w każdej chwili możesz odinstalować dodatkowe zasoby. Zanim poprosisz o odinstalowanie, możesz najpierw sprawdzić, jakie języki są obecnie zainstalowane, w następujący sposób:

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

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

Następnie możesz wybrać języki do odinstalowania, korzystając z metody deferredLanguageUninstall(), jak pokazano poniżej.

Kotlin

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

Java

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

Lokalne testowanie instalacji modułów

Biblioteka Play Feature Delivery pozwala lokalnie przetestować, czy aplikacja może wykonywać te czynności bez łączenia się ze Sklepem Play:

Z tego artykułu dowiesz się, jak wdrożyć dzielone pliki APK aplikacji na urządzeniu testowym, aby usługa Play Feature Delivery automatycznie używała tych plików APK do symulacji wysyłania żądań, pobierania i instalowania modułów ze Sklepu Play.

Chociaż nie musisz wprowadzać żadnych zmian w logice aplikacji, musisz spełnić te wymagania:

Tworzenie zestawu plików APK

Jeśli jeszcze go nie masz, utwórz dzielone pakiety APK swojej aplikacji w ten sposób:

  1. Utwórz pakiet aplikacji dla swojej aplikacji, korzystając z jednej z tych metod:
  2. Użyj tego polecenia w bundletool, aby wygenerować zestaw plików APK dla wszystkich konfiguracji urządzeń:

    bundletool build-apks --local-testing
      --bundle my_app.aab
      --output my_app.apks
    

Flaga --local-testing zawiera metadane w plikach manifestu pakietów APK, dzięki którym biblioteka Play Feature Delivery wie, że do testowania instalowania modułów funkcji bez łączenia się ze Sklepem Play może użyć lokalnych podzielonych plików APK.

Wdrażanie aplikacji na urządzeniu

Po utworzeniu zestawu plików APK przy użyciu flagi --local-testing użyj bundletool, aby zainstalować wersję podstawową aplikacji i przenieść dodatkowe pliki APK do pamięci lokalnej urządzenia. Oba te działania możesz wykonać za pomocą tego polecenia:

bundletool install-apks --apks my_app.apks

Teraz gdy uruchomisz aplikację i wykonasz instrukcje pobierania oraz instalowania modułu funkcji, biblioteka Feature Delivery korzysta z plików APK, które bundletool zostały przeniesione do pamięci lokalnej urządzenia.

Symulowanie błędu sieci

Aby symulować instalacje modułów ze Sklepu Play, biblioteka Play Feature Delivery wysyła żądania do modułu, korzystając z alternatywy dla SplitInstallManager o nazwie FakeSplitInstallManager. Jeśli używasz bundletool z flagą --local-testing do utworzenia zestawu plików APK i wdrożenia ich na urządzeniu testowym, zawiera on metadane, które instruują bibliotekę Play Feature Delivery, aby automatycznie przełączały wywołania interfejsu API aplikacji na wywołanie FakeSplitInstallManager zamiast SplitInstallManager.

FakeSplitInstallManager zawiera flagę wartości logicznej, którą możesz włączyć, aby symulować błąd sieci przy następnym żądaniu zainstalowania modułu przez aplikację. Aby uzyskać dostęp do funkcji FakeSplitInstallManager podczas testów, możesz uzyskać jej instancję przy użyciu FakeSplitInstallManagerFactory w następujący sposób:

Kotlin

// Creates an instance of FakeSplitInstallManager with the app's context.
val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context)
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true)

Java

// Creates an instance of FakeSplitInstallManager with the app's context.
FakeSplitInstallManager fakeSplitInstallManager =
    FakeSplitInstallManagerFactory.create(context);
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true);