Google Play wymaga, aby skompresowany plik APK pobierany przez użytkowników nie przekraczał 100 MB. W przypadku większości aplikacji jest to wystarczająca ilość miejsca na cały kod i komponenty aplikacji. Niektóre aplikacje potrzebują jednak więcej miejsca na grafikę o wysokiej jakości, pliki multimedialne lub inne duże zasoby. Wcześniej, jeśli skompresowany rozmiar pobierania aplikacji przekraczał 100 MB, trzeba było samodzielnie hostować i pobierać dodatkowe zasoby, gdy użytkownik otwierał aplikację. Hostowanie i udostępnianie dodatkowych plików może być kosztowne, a wrażenia użytkownika często nie są idealne. Aby ułatwić Ci ten proces i uczynić go przyjemniejszym dla użytkowników, Google Play umożliwia dołączenie 2 dużych plików rozszerzeń, które uzupełniają plik APK.
Google Play przechowuje pliki dodatkowe aplikacji i udostępnia je na urządzeniu bez żadnych opłat. Pliki rozszerzeń są zapisywane w pamięci współdzielonej urządzenia (na karcie SD lub partycji na dysku USB, zwanej też pamięcią „zewnętrzną”), z której może korzystać aplikacja. Na większości urządzeń Google Play pobiera pliki rozszerzające w tym samym czasie co plik APK, dzięki czemu aplikacja ma wszystko, czego potrzebuje, gdy użytkownik otworzy ją po raz pierwszy. W niektórych przypadkach aplikacja musi jednak pobrać pliki z Google Play po uruchomieniu.
Jeśli nie chcesz używać plików rozszerzających, a skompresowany rozmiar pobierania aplikacji jest większy niż 100 MB, prześlij aplikację za pomocą pakietów aplikacji na Androida, które umożliwiają skompresowany rozmiar pobierania do 500 MB. Dodatkowo, ponieważ używanie pakietów aplikacji odracza generowanie i podpisywanie plików APK do momentu, gdy Google Play je wygeneruje, użytkownicy pobierają zoptymalizowane pliki APK zawierające tylko kod i zasoby potrzebne do uruchomienia aplikacji. Nie musisz tworzyć, podpisywać ani zarządzać wieloma plikami APK ani plikami rozszerzeń, a użytkownicy otrzymują mniejsze i bardziej zoptymalizowane pliki do pobrania.
Przegląd
Za każdym razem, gdy przesyłasz plik APK za pomocą Konsoli Google Play, możesz dodać do niego 1 lub 2 pliki rozszerzeń. Każdy plik może mieć maksymalnie 2 GB i może być w dowolnym formacie, ale zalecamy używanie skompresowanego pliku, aby oszczędzać przepustowość podczas pobierania. Każdy plik rozszerzający pełni inną rolę:
- Główny plik rozszerzający to podstawowy plik rozszerzający zawierający dodatkowe zasoby wymagane przez aplikację.
- Plik rozszerzający z poprawką jest opcjonalny i służy do wprowadzania niewielkich aktualizacji w głównym pliku rozszerzającym.
Możesz używać tych 2 plików rozszerzających w dowolny sposób, ale zalecamy, aby główny plik rozszerzający zawierał podstawowe zasoby i był rzadko aktualizowany (lub wcale). Plik rozszerzający z poprawkami powinien być mniejszy i służyć jako „nośnik poprawek”, który jest aktualizowany przy każdej większej wersji lub w razie potrzeby.
Nawet jeśli aktualizacja aplikacji wymaga tylko nowego pliku rozszerzającego z poprawką, musisz przesłać nowy pakiet APK ze zaktualizowanym parametrem versionCode w pliku manifestu. Konsola Play nie pozwala przesyłać pliku rozszerzającego do istniejącego pakietu APK.
Uwaga: plik rozszerzający z poprawką jest semantycznie taki sam jak główny plik rozszerzający – możesz używać każdego z nich w dowolny sposób.
Format nazwy pliku
Każdy przesłany plik rozszerzający może mieć dowolny format (ZIP, PDF, MP4 itp.). Możesz też użyć narzędzia JOBB, aby spakować i zaszyfrować zestaw plików zasobów oraz kolejne poprawki do tego zestawu. Niezależnie od typu pliku Google Play traktuje je jako nieprzezroczyste obiekty binarne i zmienia ich nazwy zgodnie z tym schematem:
[main|patch].<expansion-version>.<package-name>.obb
Ten schemat składa się z 3 elementów:
mainlubpatch- Określa, czy plik jest głównym plikiem rozszerzającym czy plikiem rozszerzającym z poprawkami. Każdy plik APK może mieć tylko 1 plik główny i 1 plik z poprawką.
<expansion-version>- Jest to liczba całkowita zgodna z kodem wersji pliku APK, z którym rozszerzenie jest po raz pierwszy powiązane (odpowiada wartości
android:versionCodeaplikacji).Słowo „pierwszy” jest tu podkreślone, ponieważ chociaż Konsola Play umożliwia ponowne użycie przesłanego pliku rozszerzającego z nowym pakietem APK, nazwa pliku rozszerzającego nie zmienia się – zachowuje wersję, która została do niego zastosowana podczas pierwszego przesyłania.
<package-name>- Nazwa pakietu aplikacji w stylu Java.
Załóżmy na przykład, że wersja pakietu APK to 314159, a nazwa pakietu to com.example.app. Jeśli prześlesz główny plik rozszerzający, jego nazwa zostanie zmieniona na:
main.314159.com.example.app.obb
Lokalizacja zapisu
Gdy Google Play pobiera pliki rozszerzeń na urządzenie, zapisuje je we wspólnym miejscu pamięci systemu. Aby zapewnić prawidłowe działanie, nie możesz usuwać, przenosić ani zmieniać nazw plików rozszerzeń. Jeśli aplikacja musi pobrać pliki z Google Play, musisz zapisać je w dokładnie tej samej lokalizacji.
Metoda getObbDir() zwraca konkretną lokalizację plików rozszerzeń w tej postaci:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>to ścieżka do współdzielonego miejsca na dane dostępnego zgetExternalStorageDirectory().<package-name>to nazwa pakietu aplikacji w stylu Java, dostępna wgetPackageName().
W tym katalogu nie ma więcej niż 2 pliki rozszerzeń dla każdej aplikacji.
Jeden to główny plik rozszerzający, a drugi to plik rozszerzający z poprawką (w razie potrzeby). Poprzednie wersje zostaną zastąpione, gdy zaktualizujesz aplikację o nowe pliki rozszerzenia. Od Androida 4.4 (poziom API 19) aplikacje mogą odczytywać OBB pliki rozszerzeń bez uprawnień dostępu do pamięci zewnętrznej. Niektóre implementacje Androida 6.0 (poziom interfejsu API 23) i nowszych nadal wymagają jednak uprawnień, więc musisz zadeklarować uprawnienie READ_EXTERNAL_STORAGE w pliku manifestu aplikacji i poprosić o nie w czasie działania aplikacji w ten sposób:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
W przypadku Androida w wersji 6 i nowszej uprawnienia do pamięci zewnętrznej muszą być wymagane w czasie działania. Niektóre implementacje Androida nie wymagają jednak uprawnień do odczytywania plików OBB. Poniższy fragment kodu pokazuje, jak sprawdzić dostęp do odczytu przed poproszeniem o zezwolenie na dostęp do pamięci zewnętrznej:
Kotlin
val obb = File(obb_filename) var open_failed = false try { BufferedReader(FileReader(obb)).also { br -> ReadObbFile(br) } } catch (e: IOException) { open_failed = true } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission() }
Java
File obb = new File(obb_filename); boolean open_failed = false; try { BufferedReader br = new BufferedReader(new FileReader(obb)); open_failed = false; ReadObbFile(br); } catch (IOException e) { open_failed = true; } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission(); }
Jeśli musisz rozpakować zawartość plików rozszerzających, nie usuwaj ich później i nie zapisuj rozpakowanych danych w tym samym katalogu.OBB Rozpakowane pliki należy zapisać w katalogu określonym przez zmienną getExternalFilesDir(). Jeśli to możliwe, najlepiej używać formatu pliku rozszerzenia, który umożliwia bezpośrednie odczytywanie danych z pliku, zamiast wymagać ich rozpakowywania. Na przykład udostępniliśmy projekt biblioteki o nazwie APK Expansion Zip Library, który odczytuje dane bezpośrednio z pliku ZIP.
Ostrzeżenie: w przeciwieństwie do plików APK wszystkie pliki zapisane w pamięci współdzielonej mogą być odczytywane przez użytkownika i inne aplikacje.
Wskazówka: jeśli pakujesz pliki multimedialne do pliku ZIP, możesz używać wywołań odtwarzania multimediów w plikach z elementami sterującymi przesunięciem i długością (np. MediaPlayer.setDataSource() i SoundPool.load()) bez konieczności rozpakowywania pliku ZIP. Aby to działało, podczas tworzenia pakietów ZIP nie możesz dodatkowo kompresować plików multimedialnych. Na przykład podczas korzystania z narzędzia zip należy użyć opcji -n, aby określić sufiksy plików, które nie powinny być kompresowane:
zip -n .mp4;.ogg main_expansion media_files
Proces pobierania
Zazwyczaj Google Play pobiera i zapisuje pliki rozszerzeń w tym samym czasie, w którym pobiera plik APK na urządzenie. W niektórych przypadkach Google Play nie może pobrać plików rozszerzających lub użytkownik mógł usunąć wcześniej pobrane pliki rozszerzające. Aby sobie z tym poradzić, aplikacja musi mieć możliwość pobrania plików samodzielnie po uruchomieniu głównej aktywności, korzystając z adresu URL podanego przez Google Play.
Proces pobierania wygląda ogólnie tak:
- Użytkownik wybiera instalację aplikacji z Google Play.
- Jeśli Google Play może pobrać pliki rozszerzeń (co ma miejsce w przypadku większości urządzeń), pobiera je wraz z plikiem APK.
Jeśli Google Play nie może pobrać plików rozszerzeń, pobiera tylko pakiet APK.
- Gdy użytkownik uruchomi aplikację, musi ona sprawdzić, czy pliki rozszerzające są już zapisane na urządzeniu.
- Jeśli tak, aplikacja jest gotowa.
- Jeśli nie, aplikacja musi pobrać pliki rozszerzające przez HTTP z Google Play. Aplikacja musi wysłać żądanie do klienta Google Play za pomocą usługi Licencjonowanie aplikacji w Google Play, która odpowiada nazwą, rozmiarem pliku i adresem URL każdego pliku rozszerzającego. Na podstawie tych informacji możesz pobrać pliki i zapisać je w odpowiedniej lokalizacji.
Ostrzeżenie: konieczne jest uwzględnienie kodu, który pobiera pliki rozszerzeń z Google Play, jeśli nie ma ich jeszcze na urządzeniu w momencie uruchomienia aplikacji. Jak wspomnieliśmy w sekcji Pobieranie plików dodatkowych, udostępniliśmy bibliotekę, która znacznie upraszcza ten proces i umożliwia pobieranie z usługi przy użyciu minimalnej ilości kodu.
Lista kontrolna dewelopera
Oto podsumowanie zadań, które należy wykonać, aby używać plików rozszerzeń w aplikacji:
- Najpierw sprawdź, czy skompresowany rozmiar pliku do pobrania aplikacji musi być większy niż 100 MB. Miejsce jest cenne, więc całkowity rozmiar pobierania powinien być jak najmniejszy. Jeśli aplikacja zajmuje ponad 100 MB, ponieważ zawiera wiele wersji zasobów graficznych dla różnych gęstości ekranu, rozważ opublikowanie wielu plików APK, z których każdy zawiera tylko zasoby wymagane na ekranach, do których jest przeznaczony. Aby uzyskać najlepsze wyniki podczas publikowania w Google Play, prześlij pakiet Android App Bundle, który zawiera cały skompilowany kod i wszystkie zasoby aplikacji, ale generowanie i podpisywanie plików APK jest odroczone do momentu publikacji w Google Play.
- Określ, które zasoby aplikacji chcesz oddzielić od pliku APK, i spakuj je w plik, który będzie używany jako główny plik rozszerzający.
Zwykle drugiego pliku rozszerzającego z poprawką należy używać tylko podczas aktualizowania głównego pliku rozszerzającego. Jeśli jednak zasoby przekraczają limit 2 GB w przypadku głównego pliku rozszerzającego, możesz użyć pliku z poprawką dla pozostałych zasobów.
- Opracuj aplikację tak, aby korzystała z zasobów z plików rozszerzeń w pamięci współdzielonej urządzenia.
Pamiętaj, że nie możesz usuwać, przenosić ani zmieniać nazw plików rozszerzeń.
Jeśli Twoja aplikacja nie wymaga określonego formatu, zalecamy utworzenie plików ZIP dla plików rozszerzenia, a następnie odczytanie ich za pomocą biblioteki APK Expansion Zip.
- Dodaj do głównej aktywności aplikacji logikę, która sprawdza, czy pliki rozszerzeń są na urządzeniu podczas uruchamiania. Jeśli plików nie ma na urządzeniu, użyj usługi licencjonowania aplikacji w Google Play, aby poprosić o adresy URL plików dodatkowych, a następnie pobierz je i zapisz.
Aby znacznie zmniejszyć ilość kodu, który musisz napisać, i zapewnić użytkownikom wygodę podczas pobierania, zalecamy użycie biblioteki Downloader do wdrożenia funkcji pobierania.
Jeśli zamiast biblioteki utworzysz własną usługę pobierania, pamiętaj, że nie możesz zmieniać nazw plików rozszerzeń i musisz zapisywać je w odpowiednim miejscu na dane.
Po zakończeniu tworzenia aplikacji postępuj zgodnie z instrukcjami w artykule Testowanie plików rozszerzających.
Zasady i ograniczenia
Dodawanie plików rozszerzeń APK to funkcja dostępna podczas przesyłania aplikacji za pomocą Konsoli Play. Podczas przesyłania aplikacji po raz pierwszy lub aktualizowania aplikacji, która korzysta z plików rozszerzeń, musisz pamiętać o tych zasadach i ograniczeniach:
- Każdy plik rozszerzający może mieć maksymalnie 2 GB.
- Aby pobrać pliki rozszerzeń z Google Play, użytkownik musi mieć aplikację pobraną z Google Play. Google Play nie udostępni adresów URL plików rozszerzeń, jeśli aplikacja została zainstalowana w inny sposób.
- Podczas pobierania z poziomu aplikacji adres URL, który Google Play udostępnia dla każdego pliku, jest unikalny dla każdego pobierania i wygasa wkrótce po przekazaniu go do aplikacji.
- Jeśli zaktualizujesz aplikację za pomocą nowego pliku APK lub prześlesz wiele plików APK dla tej samej aplikacji, możesz wybrać pliki rozszerzające, które zostały przesłane dla poprzedniego pliku APK. Nazwa pliku rozszerzenia nie zmienia się – zachowuje wersję otrzymaną przez plik APK, z którym była pierwotnie powiązana.
- Jeśli używasz plików rozszerzeń w połączeniu z wieloma plikami APK, aby udostępniać różne pliki rozszerzeń na różnych urządzeniach, musisz przesłać osobne pliki APK na każde urządzenie, aby podać unikalną wartość
versionCodei zadeklarować różne filtry dla każdego pliku APK. - Nie możesz zaktualizować aplikacji, zmieniając tylko pliki rozszerzające – musisz przesłać nowy plik APK. Jeśli zmiany dotyczą tylko zasobów w plikach rozszerzających, możesz zaktualizować plik APK, zmieniając tylko
versionCode(a być może takżeversionName). - Nie zapisuj innych danych w
obb/katalogu. Jeśli musisz rozpakować niektóre dane, zapisz je w lokalizacji określonej przezgetExternalFilesDir(). - Nie usuwaj ani nie zmieniaj nazwy pliku rozszerzającego
.obb(chyba że przeprowadzasz aktualizację). Spowoduje to wielokrotne pobieranie pliku rozszerzającego przez Google Play (lub samą aplikację). - Podczas ręcznego aktualizowania pliku rozszerzającego musisz usunąć poprzedni plik rozszerzający.
Pobieranie plików rozszerzeń
W większości przypadków Google Play pobiera i zapisuje pliki rozszerzeń na urządzeniu w tym samym czasie, w którym instaluje lub aktualizuje plik APK. Dzięki temu pliki rozszerzeń będą dostępne, gdy aplikacja zostanie uruchomiona po raz pierwszy. W niektórych przypadkach aplikacja musi sama pobrać pliki rozszerzające, wysyłając żądanie na adres URL podany w odpowiedzi usługi licencjonowania aplikacji w Google Play.
Podstawowa logika pobierania plików rozszerzających jest następująca:
- Gdy aplikacja się uruchomi, poszukaj plików rozszerzających w pamięci współdzielonej (w katalogu
Android/obb/<package-name>/).- Jeśli pliki dodatkowe są obecne, wszystko jest w porządku i aplikacja może kontynuować działanie.
- Jeśli plików rozszerzenia nie ma:
- Wyślij żądanie za pomocą licencjonowania aplikacji w Google Play, aby uzyskać nazwy, rozmiary i adresy URL plików rozszerzających aplikacji.
- Użyj adresów URL podanych przez Google Play, aby pobrać pliki rozszerzeń i je zapisać. Pliki musisz zapisać w lokalizacji pamięci współdzielonej (
Android/obb/<package-name>/) i użyć dokładnej nazwy pliku podanej w odpowiedzi Google Play.Uwaga: adres URL, który Google Play udostępnia dla plików rozszerzających, jest unikalny dla każdego pobierania i wygasa wkrótce po przekazaniu go aplikacji.
Jeśli Twoja aplikacja jest bezpłatna (nie jest płatna), prawdopodobnie nie korzystasz z usługi licencjonowania aplikacji. Została ona zaprojektowana przede wszystkim po to, aby umożliwić Ci egzekwowanie zasad licencjonowania aplikacji i upewnienie się, że użytkownik ma prawo do korzystania z niej (zapłacił za nią w Google Play). Aby ułatwić korzystanie z funkcji plików rozszerzających, usługa licencjonowania została ulepszona i odpowiada na zapytania aplikacji, podając adres URL plików rozszerzających aplikacji hostowanych w Google Play. Nawet jeśli Twoja aplikacja jest bezpłatna dla użytkowników, musisz dołączyć bibliotekę weryfikacji licencji (LVL), aby używać plików rozszerzeń APK. Jeśli Twoja aplikacja jest bezpłatna, nie musisz wymuszać weryfikacji licencji. Wystarczy, że biblioteka wykona żądanie, które zwraca adres URL plików dodatkowych.
Uwaga: niezależnie od tego, czy Twoja aplikacja jest bezpłatna, Google Play zwraca adresy URL plików rozszerzeń tylko wtedy, gdy użytkownik pobrał aplikację z Google Play.
Oprócz LVL potrzebujesz zestawu kodu, który pobiera pliki rozszerzeń przez połączenie HTTP i zapisuje je w odpowiednim miejscu w pamięci współdzielonej urządzenia. Podczas wdrażania tej procedury w aplikacji należy wziąć pod uwagę kilka kwestii:
- Na urządzeniu może nie być wystarczająco dużo miejsca na pliki dodatkowe, więc przed rozpoczęciem pobierania sprawdź, czy jest wystarczająco dużo miejsca, i ostrzeż użytkownika, jeśli nie.
- Pobieranie plików powinno odbywać się w usłudze w tle, aby nie blokować interakcji użytkownika i umożliwić mu opuszczenie aplikacji podczas pobierania.
- Podczas przesyłania żądania i pobierania danych może wystąpić wiele błędów, które musisz odpowiednio obsłużyć.
- Podczas pobierania połączenie sieciowe może się zmienić, dlatego należy obsługiwać takie zmiany i w razie przerwania pobierania wznowić je, gdy będzie to możliwe.
- Pobieranie odbywa się w tle, ale musisz wyświetlać powiadomienie, które informuje o postępach pobierania, powiadamia użytkownika o zakończeniu pobierania i po wybraniu przenosi go z powrotem do aplikacji.
Aby ułatwić Ci to zadanie, stworzyliśmy bibliotekę pobierania, która wysyła żądania adresów URL plików rozszerzających za pomocą usługi licencjonowania, pobiera pliki rozszerzające, wykonuje wszystkie wymienione wyżej zadania, a nawet umożliwia wstrzymywanie i wznawianie pobierania. Po dodaniu do aplikacji biblioteki Downloader Library i kilku punktów zaczepienia kodu większość pracy związanej z pobieraniem plików dodatkowych jest już wykonana. Dlatego, aby zapewnić użytkownikom jak najlepsze wrażenia przy minimalnym nakładzie pracy z Twojej strony, zalecamy używanie biblioteki pobierania do pobierania plików dodatkowych. Informacje w sekcjach poniżej wyjaśniają, jak zintegrować bibliotekę z aplikacją.
Jeśli wolisz opracować własne rozwiązanie do pobierania plików rozszerzeń za pomocą adresów URL Google Play, musisz wykonać prośbę o licencję zgodnie z dokumentacją licencjonowania aplikacji, a następnie pobrać nazwy, rozmiary i adresy URL plików rozszerzeń z dodatkowych informacji w odpowiedzi. Jako zasady licencjonowania użyj klasy APKExpansionPolicy (zawartej w bibliotece weryfikacji licencji), która pobiera nazwy, rozmiary i adresy URL plików rozszerzeń z usługi licencjonowania.
Informacje o bibliotece pobierania
Aby używać plików rozszerzeń APK w aplikacji i zapewnić użytkownikom jak najlepsze wrażenia przy minimalnym wysiłku z Twojej strony, zalecamy korzystanie z biblioteki pobierania zawartej w pakiecie biblioteki rozszerzeń APK Google Play. Ta biblioteka pobiera pliki dodatkowe w usłudze działającej w tle, wyświetla powiadomienie o stanie pobierania, obsługuje utratę połączenia z siecią, wznawia pobieranie, gdy jest to możliwe, i wykonuje inne czynności.
Aby wdrożyć pobieranie plików rozszerzających za pomocą biblioteki Downloader Library, wystarczy:
- Rozszerz specjalną
ServicepodklasęBroadcastReceiver, która wymaga tylko kilku wierszy kodu. - Dodaj do głównej aktywności logikę, która sprawdza, czy pliki rozszerzeń zostały już pobrane. Jeśli nie, wywołuje proces pobierania i wyświetla interfejs postępu.
- W głównej aktywności zaimplementuj interfejs wywołania zwrotnego z kilkoma metodami, które będą otrzymywać aktualizacje dotyczące postępu pobierania.
W sekcjach poniżej znajdziesz informacje o tym, jak skonfigurować aplikację za pomocą biblioteki pobierania.
Przygotowanie do korzystania z biblioteki pobierania
Aby korzystać z biblioteki pobierania, musisz pobrać 2 pakiety z SDK Manager i dodać odpowiednie biblioteki do aplikacji.
Najpierw otwórz Menedżera SDK Androida (Narzędzia > Menedżer SDK) i w sekcji Wygląd i zachowanie > Ustawienia systemu > Android SDK kliknij kartę SDK Tools, aby wybrać i pobrać:
- Pakiet Biblioteki licencji Google Play
- Pakiet biblioteki rozszerzeń APK Google Play
Utwórz nowy moduł biblioteki dla biblioteki weryfikacji licencji i biblioteki pobierania. W przypadku każdej biblioteki:
- Wybierz Plik > Nowy > Nowy moduł.
- W oknie Create New Module (Utwórz nowy moduł) kliknij Android Library (Biblioteka Androida), a potem Next (Dalej).
- Podaj nazwę aplikacji lub biblioteki, np. „Biblioteka licencji Google Play” i „Biblioteka pobierania Google Play”, wybierz minimalny poziom SDK, a potem kliknij Zakończ.
- Wybierz Plik > Struktura projektu.
- Wybierz kartę Właściwości i w sekcji Biblioteka
Repozytorium wpisz bibliotekę z katalogu
<sdk>/extras/google/(play_licensing/w przypadku biblioteki weryfikacji licencji lubplay_apk_expansion/downloader_library/w przypadku biblioteki pobierania). - Aby utworzyć nowy moduł, kliknij OK.
Uwaga: biblioteka pobierania zależy od biblioteki weryfikacji licencji. Pamiętaj, aby dodać bibliotekę weryfikacji licencji do właściwości projektu biblioteki pobierania.
Możesz też zaktualizować projekt, aby uwzględniał biblioteki, korzystając z wiersza poleceń:
- Przejdź do katalogu
<sdk>/tools/. - Wykonaj polecenie
android update projectz opcją--library, aby dodać do projektu zarówno bibliotekę LVL, jak i bibliotekę pobierania. Przykład:android update project --path ~/Android/MyApp \ --library ~/android_sdk/extras/google/market_licensing \ --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
Po dodaniu do aplikacji zarówno Biblioteki weryfikacji licencji, jak i Biblioteki pobierania możesz szybko zintegrować możliwość pobierania plików dodatkowych z Google Play. Format wybrany dla plików rozszerzeń i sposób ich odczytywania z pamięci współdzielonej to osobna implementacja, którą należy rozważyć w zależności od potrzeb aplikacji.
Wskazówka: pakiet rozszerzenia APK zawiera przykładową aplikację, która pokazuje, jak używać w aplikacji biblioteki pobierania. Przykład korzysta z biblioteki innej firmy dostępnej w pakiecie rozszerzenia APK o nazwie APK Expansion Zip Library. Jeśli planujesz używać plików ZIP jako plików rozszerzeń, zalecamy dodanie do aplikacji biblioteki APK Expansion Zip Library. Więcej informacji znajdziesz w sekcji poniżej Korzystanie z biblioteki APK Expansion Zip Library.
Deklarowanie uprawnień użytkownika
Aby pobrać pliki rozszerzające, biblioteka pobierania wymaga kilku uprawnień, które musisz zadeklarować w pliku manifestu aplikacji. Są to:
<manifest ...> <!-- Required to access Google Play Licensing --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> <!-- Required to download files from Google Play --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Required to poll the state of the network connection and respond to changes --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Required to check whether Wi-Fi is enabled --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- Required to read and write the expansion files on shared storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
Uwaga: domyślnie biblioteka pobierania wymaga interfejsu API na poziomie 4, a biblioteka rozszerzenia APK wymaga interfejsu API na poziomie 5.
Implementowanie usługi pobierania
Aby pobierać pliki w tle, biblioteka Downloader udostępnia własną podklasę Service o nazwie DownloaderService, którą należy rozszerzyć. Oprócz pobierania plików rozszerzeń DownloaderService:
- Rejestruje
BroadcastReceiver, który nasłuchuje zmian w połączeniu sieciowym urządzenia (CONNECTIVITY_ACTION), aby w razie potrzeby wstrzymać pobieranie (np. z powodu utraty połączenia) i wznowić je, gdy będzie to możliwe (po uzyskaniu połączenia). - Planuje alarm
RTC_WAKEUP, aby ponowić pobieranie w przypadku, gdy usługa zostanie zamknięta. - Tworzy niestandardowy element
Notification, który wyświetla postęp pobierania oraz wszelkie błędy lub zmiany stanu. - Umożliwia aplikacji ręczne wstrzymywanie i wznawianie pobierania.
- Sprawdza, czy pamięć współdzielona jest zamontowana i dostępna, czy pliki jeszcze nie istnieją i czy jest wystarczająco dużo miejsca. Wszystko to przed pobraniem plików rozszerzeń. Następnie powiadamia użytkownika, jeśli któreś z tych stwierdzeń jest nieprawdziwe.
Wystarczy utworzyć w aplikacji klasę, która rozszerza klasę DownloaderService, i zastąpić 3 metody, aby podać szczegółowe informacje o aplikacji:
getPublicKey()- Ta funkcja musi zwracać ciąg znaków, który jest kluczem publicznym RSA zakodowanym algorytmem Base64 dla Twojego konta wydawcy. Jest on dostępny na stronie profilu w Konsoli Play (patrz Konfigurowanie licencji).
getSALT()- Musi zwracać tablicę losowych bajtów, których licencjonowanie
Policyużywa do tworzeniaObfuscator. Salt zapewnia, że zaciemniony plikSharedPreferences, w którym zapisane są dane licencyjne, będzie unikalny i niemożliwy do wykrycia. getAlarmReceiverClassName()- Musi to zwracać nazwę klasy
BroadcastReceiverw aplikacji, która powinna otrzymywać alarm wskazujący, że pobieranie należy ponownie uruchomić (co może się zdarzyć, jeśli usługa pobierania nieoczekiwanie się zatrzyma).
Oto przykład pełnej implementacji właściwości DownloaderService:
Kotlin
// You must use the public key belonging to your publisher account const val BASE64_PUBLIC_KEY = "YourLVLKey" // You should also modify this salt val SALT = byteArrayOf( 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 ) class SampleDownloaderService : DownloaderService() { override fun getPublicKey(): String = BASE64_PUBLIC_KEY override fun getSALT(): ByteArray = SALT override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name }
Java
public class SampleDownloaderService extends DownloaderService { // You must use the public key belonging to your publisher account public static final String BASE64_PUBLIC_KEY = "YourLVLKey"; // You should also modify this salt public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 }; @Override public String getPublicKey() { return BASE64_PUBLIC_KEY; } @Override public byte[] getSALT() { return SALT; } @Override public String getAlarmReceiverClassName() { return SampleAlarmReceiver.class.getName(); } }
Uwaga: musisz zaktualizować wartość BASE64_PUBLIC_KEY, aby była kluczem publicznym należącym do Twojego konta wydawcy. Klucz znajdziesz w Konsoli dewelopera w informacjach o profilu. Jest to konieczne nawet podczas testowania pobrań.
Pamiętaj, aby zadeklarować usługę w pliku manifestu:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Implementowanie odbiornika alarmu
Aby monitorować postęp pobierania plików i w razie potrzeby ponownie uruchamiać pobieranie, DownloaderService planuje alarm RTC_WAKEUP, który wysyła Intent do BroadcastReceiver w Twojej aplikacji. Musisz zdefiniować BroadcastReceiver, aby wywołać interfejs API z biblioteki Downloader, który sprawdza stan pobierania i w razie potrzeby ponownie je uruchamia.
Wystarczy zastąpić metodę onReceive(), aby wywołać DownloaderClientMarshaller.startDownloadServiceIfRequired().
Przykład:
Kotlin
class SampleAlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired( context, intent, SampleDownloaderService::class.java ) } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() } } }
Java
public class SampleAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, SampleDownloaderService.class); } catch (NameNotFoundException e) { e.printStackTrace(); } } }
Zwróć uwagę, że jest to klasa, dla której musisz zwrócić nazwę w metodzie getAlarmReceiverClassName() usługi (patrz poprzednia sekcja).
Pamiętaj, aby zadeklarować odbiorcę w pliku manifestu:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Rozpoczynanie pobierania
Główna aktywność w aplikacji (ta, która jest uruchamiana przez ikonę programu uruchamiającego) jest odpowiedzialna za sprawdzenie, czy pliki rozszerzające są już na urządzeniu, i za rozpoczęcie pobierania, jeśli ich nie ma.
Aby rozpocząć pobieranie za pomocą biblioteki Downloader, wykonaj te czynności:
- Sprawdź, czy pliki zostały pobrane.
Biblioteka pobierania zawiera kilka interfejsów API w klasie
Helper, które pomagają w tym procesie:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)doesFileExist(Context c, String fileName, long fileSize)
Na przykład przykładowa aplikacja w pakiecie rozszerzenia APK wywołuje w metodzie
onCreate()działania tę metodę, aby sprawdzić, czy pliki rozszerzające są już na urządzeniu:Kotlin
fun expansionFilesDelivered(): Boolean { xAPKS.forEach { xf -> Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName -> if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false } } return true }
Java
boolean expansionFilesDelivered() { for (XAPKFile xf : xAPKS) { String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion); if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false; } return true; }
W tym przypadku każdy obiekt
XAPKFilezawiera numer wersji i rozmiar znanego pliku rozszerzającego oraz wartość logiczną określającą, czy jest to główny plik rozszerzający. (Szczegóły znajdziesz w klasieSampleDownloaderActivityprzykładowej aplikacji).Jeśli ta metoda zwróci wartość false, aplikacja musi rozpocząć pobieranie.
- Rozpocznij pobieranie, wywołując metodę statyczną
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass).Metoda przyjmuje te parametry:
context:ContextTwojej aplikacji.notificationClient:PendingIntent, aby rozpocząć główną aktywność. Jest on używany wNotification, któreDownloaderServicetworzy, aby pokazywać postęp pobierania. Gdy użytkownik wybierze powiadomienie, system wywoła podaną tu funkcjęPendingIntenti powinien otworzyć aktywność, która pokazuje postęp pobierania (zwykle jest to ta sama aktywność, która rozpoczęła pobieranie).serviceClass: obiektClassw przypadku implementacjiDownloaderService, wymagany do uruchomienia usługi i rozpoczęcia pobierania w razie potrzeby.
Metoda zwraca liczbę całkowitą, która wskazuje, czy pobieranie jest wymagane. Możliwe wartości to:
NO_DOWNLOAD_REQUIRED: zwracany, jeśli pliki już istnieją lub pobieranie jest w toku.LVL_CHECK_REQUIRED: zwracany, jeśli do uzyskania adresów URL plików rozszerzających wymagana jest weryfikacja licencji.DOWNLOAD_REQUIRED: zwracany, jeśli adresy URL plików rozszerzających są już znane, ale nie zostały pobrane.
Atrybuty
LVL_CHECK_REQUIREDiDOWNLOAD_REQUIREDdziałają w zasadzie tak samo i zwykle nie musisz się nimi przejmować. W głównej aktywności, która wywołuje funkcjęstartDownloadServiceIfRequired(), możesz po prostu sprawdzić, czy odpowiedź ma wartośćNO_DOWNLOAD_REQUIRED. Jeśli odpowiedź jest inna niżNO_DOWNLOAD_REQUIRED, biblioteka pobierania rozpoczyna pobieranie i powinieneś(-aś) zaktualizować interfejs aktywności, aby wyświetlać postęp pobierania (patrz następny krok). Jeśli odpowiedź toNO_DOWNLOAD_REQUIRED, pliki są dostępne i aplikacja może się uruchomić.Przykład:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { val pendingIntent = // Build an Intent to start this activity from the Notification Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP }.let { notifierIntent -> PendingIntent.getActivity( this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT ) } // Start the download service (if required) val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired( this, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return } // If the download wasn't necessary, fall through to start the app } startApp() // Expansion files are available, start the app }
Java
@Override public void onCreate(Bundle savedInstanceState) { // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { // Build an Intent to start this activity from the Notification Intent notifierIntent = new Intent(this, MainActivity.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); ... PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return; } // If the download wasn't necessary, fall through to start the app } startApp(); // Expansion files are available, start the app }
- Jeśli metoda
startDownloadServiceIfRequired()zwróci wartość inną niżNO_DOWNLOAD_REQUIRED, utwórz instancjęIStub, wywołującDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService).IStubzapewnia powiązanie między Twoją aktywnością a usługą pobierania, dzięki czemu Twoja aktywność otrzymuje wywołania zwrotne dotyczące postępu pobierania.Aby utworzyć instancję
IStubprzez wywołanieCreateStub(), musisz przekazać jej implementację interfejsuIDownloaderClienti implementacjęDownloaderService. W następnej sekcji Otrzymywanie informacji o postępie pobierania omawiamy interfejsIDownloaderClient, który zwykle należy zaimplementować w klasieActivity, aby można było aktualizować interfejs aktywności, gdy zmieni się stan pobierania.Zalecamy wywołanie funkcji
CreateStub()w celu utworzenia instancjiIStubw metodzieonCreate()działania po tym, jakstartDownloadServiceIfRequired()rozpocznie pobieranie.Na przykład w poprzednim przykładowym kodzie dla
onCreate()możesz odpowiedzieć na wynikstartDownloadServiceIfRequired()w ten sposób:Kotlin
// Start the download service (if required) val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( this@MainActivity, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java) // Inflate layout that shows download progress setContentView(R.layout.downloader_ui) return }
Java
// Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService.class); // Inflate layout that shows download progress setContentView(R.layout.downloader_ui); return; }
Po powrocie metody
onCreate()Twoja aktywność otrzyma wywołanie metodyonResume(), w której należy wywołać metodęconnect()na obiekcieIStub, przekazując jej obiektContextaplikacji. Z kolei w wywołaniu zwrotnymonStop()aktywności należy wywołać funkcjędisconnect().Kotlin
override fun onResume() { downloaderClientStub?.connect(this) super.onResume() } override fun onStop() { downloaderClientStub?.disconnect(this) super.onStop() }
Java
@Override protected void onResume() { if (null != downloaderClientStub) { downloaderClientStub.connect(this); } super.onResume(); } @Override protected void onStop() { if (null != downloaderClientStub) { downloaderClientStub.disconnect(this); } super.onStop(); }
Wywołanie
connect()naIStubwiąże Twoją aktywność zDownloaderService, dzięki czemu Twoja aktywność otrzymuje wywołania zwrotne dotyczące zmian stanu pobierania za pomocą interfejsuIDownloaderClient.
Odbieranie postępu pobierania
Aby otrzymywać aktualizacje dotyczące postępu pobierania i korzystać z DownloaderService, musisz wdrożyć interfejs IDownloaderClient Biblioteki pobierania.
Zwykle aktywność, której używasz do rozpoczęcia pobierania, powinna implementować ten interfejs, aby wyświetlać postęp pobierania i wysyłać żądania do usługi.
Wymagane metody interfejsu dla IDownloaderClient to:
onServiceConnected(Messenger m)- Po utworzeniu instancji
IStubw aktywności otrzymasz wywołanie tej metody, która przekazuje obiektMessengerpołączony z Twoją instancjąDownloaderService. Aby wysyłać żądania do usługi, np. wstrzymywać i wznawiać pobieranie, musisz wywołać funkcjęDownloaderServiceMarshaller.CreateProxy(), aby otrzymać interfejsIDownloaderServicepołączony z usługą.Zalecana implementacja wygląda tak:
Kotlin
private var remoteService: IDownloaderService? = null ... override fun onServiceConnected(m: Messenger) { remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { downloaderClientStub?.messenger?.also { messenger -> onClientUpdated(messenger) } } }
Java
private IDownloaderService remoteService; ... @Override public void onServiceConnected(Messenger m) { remoteService = DownloaderServiceMarshaller.CreateProxy(m); remoteService.onClientUpdated(downloaderClientStub.getMessenger()); }
Po zainicjowaniu obiektu
IDownloaderServicemożesz wysyłać polecenia do usługi pobierania, np. wstrzymywać i wznawiać pobieranie (requestPauseDownload()irequestContinueDownload()). onDownloadStateChanged(int newState)- Usługa pobierania wywołuje tę funkcję, gdy nastąpi zmiana stanu pobierania, np. gdy pobieranie się rozpocznie lub zakończy.
Wartość
newStatebędzie jedną z kilku możliwych wartości określonych w jednej ze stałychSTATE_*klasyIDownloaderClient.Aby wyświetlić użytkownikom przydatny komunikat, możesz poprosić o odpowiedni ciąg znaków dla każdego stanu, wywołując funkcję
Helpers.getDownloaderStringResourceIDFromState(). Ta funkcja zwraca identyfikator zasobu jednego z ciągów tekstowych dołączonych do biblioteki Downloader. Na przykład ciąg znaków „Pobieranie wstrzymane z powodu roamingu” odpowiada wartościSTATE_PAUSED_ROAMING. onDownloadProgress(DownloadProgressInfo progress)- Usługa pobierania wywołuje tę funkcję, aby dostarczyć
DownloadProgressInfoobiekt, który zawiera różne informacje o postępie pobierania, w tym szacowany pozostały czas, bieżącą szybkość, ogólny postęp i całkowity postęp, dzięki czemu możesz aktualizować interfejs postępu pobierania.
Wskazówka: przykłady tych wywołań zwrotnych, które aktualizują interfejs postępu pobierania, znajdziesz w SampleDownloaderActivity w przykładowej aplikacji dostarczonej z pakietem rozszerzenia APK.
Oto niektóre publiczne metody interfejsu IDownloaderService, które mogą Ci się przydać:
requestPauseDownload()- Wstrzymuje pobieranie.
requestContinueDownload()- Wznawia wstrzymane pobieranie.
setDownloadFlags(int flags)- Ustawia preferencje użytkownika dotyczące typów sieci, w których można pobierać pliki. Obecna implementacja obsługuje 1 flagę,
FLAGS_DOWNLOAD_OVER_CELLULAR, ale możesz dodać inne. Domyślnie ta flaga jest wyłączona, więc użytkownik musi mieć połączenie Wi-Fi, aby pobrać pliki rozszerzeń. Możesz udostępnić użytkownikowi ustawienie umożliwiające pobieranie przez sieć komórkową. W takim przypadku możesz zadzwonić pod numer:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
Korzystanie z obiektu APKExpansionPolicy
Jeśli zdecydujesz się utworzyć własną usługę pobierania zamiast korzystać z biblioteki pobierania Google Play, nadal musisz używać APKExpansionPolicy udostępnionego w bibliotece weryfikacji licencji. Klasa APKExpansionPolicy jest niemal identyczna z klasą ServerManagedPolicy (dostępną w bibliotece weryfikacji licencji Google Play), ale zawiera dodatkową obsługę dodatków do odpowiedzi z pliku rozszerzenia APK.
Uwaga: jeśli używasz biblioteki pobierania w sposób opisany w poprzedniej sekcji, biblioteka wykonuje wszystkie interakcje z APKExpansionPolicy, więc nie musisz używać tej klasy bezpośrednio.
Klasa zawiera metody, które pomagają uzyskać niezbędne informacje o dostępnych plikach rozszerzeń:
getExpansionURLCount()getExpansionURL(int index)getExpansionFileName(int index)getExpansionFileSize(int index)
Więcej informacji o tym, jak używać APKExpansionPolicy, gdy nie korzystasz z biblioteki pobierania, znajdziesz w dokumentacji Dodawanie licencji do aplikacji. Wyjaśnia ona, jak wdrożyć zasady licencjonowania, takie jak te.
Odczytywanie pliku rozszerzenia
Po zapisaniu plików rozszerzeń APK na urządzeniu sposób odczytywania plików zależy od ich typu. Jak wspomnieliśmy w omówieniu, pliki rozszerzające mogą być dowolnego typu, ale ich nazwy muszą być zgodne z określonym formatem, a same pliki są zapisywane w folderze <shared-storage>/Android/obb/<package-name>/.
Niezależnie od tego, jak odczytujesz pliki, zawsze najpierw sprawdzaj, czy pamięć zewnętrzna jest dostępna do odczytu. Istnieje możliwość, że użytkownik podłączył pamięć do komputera przez USB lub wyjął kartę SD.
Uwaga: po uruchomieniu aplikacji zawsze sprawdzaj, czy pamięć zewnętrzna jest dostępna i czy można z niej odczytywać dane. W tym celu wywołaj funkcję getExternalStorageState(). Zwraca jeden z kilku możliwych ciągów znaków, które reprezentują stan pamięci zewnętrznej. Aby aplikacja mogła odczytać zwracaną wartość, musi ona mieć postać MEDIA_MOUNTED.
Pobieranie nazw plików
Zgodnie z omówieniem pliki rozszerzające APK są zapisywane w określonym formacie nazwy pliku:
[main|patch].<expansion-version>.<package-name>.obb
Aby uzyskać lokalizację i nazwy plików rozszerzeń, użyj metod getExternalStorageDirectory() i getPackageName() do utworzenia ścieżki do plików.
Oto metoda, której możesz użyć w aplikacji, aby uzyskać tablicę zawierającą pełną ścieżkę do obu plików rozszerzeń:
Kotlin
fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> { val packageName = ctx.packageName val ret = mutableListOf<String>() if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { // Build the full path to the app's expansion files val root = Environment.getExternalStorageDirectory() val expPath = File(root.toString() + EXP_PATH + packageName) // Check that expansion file path exists if (expPath.exists()) { if (mainVersion > 0) { val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb" val main = File(strMainPath) if (main.isFile) { ret += strMainPath } } if (patchVersion > 0) { val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb" val main = File(strPatchPath) if (main.isFile) { ret += strPatchPath } } } } return ret.toTypedArray() }
Java
// The shared path to all app expansion files private final static String EXP_PATH = "/Android/obb/"; static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) { String packageName = ctx.getPackageName(); Vector<String> ret = new Vector<String>(); if (Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)) { // Build the full path to the app's expansion files File root = Environment.getExternalStorageDirectory(); File expPath = new File(root.toString() + EXP_PATH + packageName); // Check that expansion file path exists if (expPath.exists()) { if ( mainVersion > 0 ) { String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb"; File main = new File(strMainPath); if ( main.isFile() ) { ret.add(strMainPath); } } if ( patchVersion > 0 ) { String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb"; File main = new File(strPatchPath); if ( main.isFile() ) { ret.add(strPatchPath); } } } } String[] retArray = new String[ret.size()]; ret.toArray(retArray); return retArray; }
Możesz wywołać tę metodę, przekazując jej aplikacjęContexti wersję wybranego pliku rozszerzenia.
Numer wersji pliku rozszerzającego można określić na wiele sposobów. Jednym z prostszych sposobów jest zapisanie wersji w pliku SharedPreferences, gdy rozpocznie się pobieranie. W tym celu należy wysłać zapytanie o nazwę pliku rozszerzającego za pomocą metody getExpansionFileName(int index) klasy APKExpansionPolicy. Kod wersji możesz uzyskać, odczytując plik SharedPreferences, gdy chcesz uzyskać dostęp do pliku rozszerzenia.
Więcej informacji o odczytywaniu danych z pamięci współdzielonej znajdziesz w dokumentacji Data Storage.
Korzystanie z biblioteki APK Expansion Zip Library
Pakiet rozszerzenia APK Google Play zawiera bibliotekę o nazwie APK Expansion Zip Library (znajdującą się w <sdk>/extras/google/google_market_apk_expansion/zip_file/). Jest to biblioteka opcjonalna, która pomaga odczytywać pliki rozszerzenia zapisane jako pliki ZIP. Korzystanie z tej biblioteki umożliwia łatwe odczytywanie zasobów z plików rozszerzeń ZIP jako wirtualnego systemu plików.
Biblioteka APK Expansion Zip Library zawiera te klasy i interfejsy API:
APKExpansionSupport- Zawiera metody dostępu do nazw plików rozszerzających i plików ZIP:
getAPKExpansionFiles()- Ta sama metoda co powyżej, która zwraca pełną ścieżkę do obu plików rozszerzeń.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)- Zwraca
ZipResourceFilereprezentujący sumę pliku głównego i pliku z poprawkami. Jeśli określisz zarównomainVersion, jak ipatchVersion, funkcja zwróciZipResourceFile, który zapewnia dostęp do odczytu wszystkich danych, przy czym dane z pliku poprawki są scalane z danymi z pliku głównego.
ZipResourceFile- Reprezentuje plik ZIP na pamięci współdzielonej i wykonuje wszystkie czynności, aby udostępnić wirtualny system plików oparty na plikach ZIP. Instancję możesz uzyskać za pomocą metody
APKExpansionSupport.getAPKExpansionZipFile()lubZipResourceFile, przekazując jej ścieżkę do pliku rozszerzającego. Ta klasa zawiera wiele przydatnych metod, ale zwykle nie musisz uzyskiwać dostępu do większości z nich. Oto kilka ważnych metod:getInputStream(String assetPath)- Umożliwia
InputStreamodczytanie pliku w pliku ZIP.assetPathmusi być ścieżką do wybranego pliku, względną w stosunku do katalogu głównego zawartości pliku ZIP. getAssetFileDescriptor(String assetPath)- Zawiera
AssetFileDescriptordla pliku w pliku ZIP.assetPathmusi być ścieżką do wybranego pliku, względną wobec katalogu głównego zawartości pliku ZIP. Jest to przydatne w przypadku niektórych interfejsów Android API, które wymagająAssetFileDescriptor, np. niektórych interfejsówMediaPlayerAPI.
APEZProvider- Większość aplikacji nie musi używać tej klasy. Ta klasa definiuje
ContentProvider, który przekazuje dane z plików ZIP za pomocą dostawcy treściUri, aby zapewnić dostęp do plików w przypadku niektórych interfejsów API Androida, które oczekują dostępuUrido plików multimedialnych. Jest to przydatne na przykład wtedy, gdy chcesz odtworzyć film zVideoView.setVideoURI().
Pomijanie kompresji ZIP plików multimedialnych
Jeśli używasz plików rozszerzeń do przechowywania plików multimedialnych, plik ZIP nadal umożliwia korzystanie z wywołań odtwarzania multimediów na Androidzie, które zapewniają kontrolę nad przesunięciem i długością (np. MediaPlayer.setDataSource() i SoundPool.load()). Aby to działało, podczas tworzenia pakietów ZIP nie możesz dodatkowo kompresować plików multimedialnych. Na przykład podczas korzystania z narzędzia zip należy użyć opcji -n, aby określić sufiksy plików, które nie powinny być kompresowane:
zip -n .mp4;.ogg main_expansion media_files
Odczytywanie z pliku ZIP
Jeśli używasz biblioteki APK Expansion Zip Library, odczytanie pliku z pliku ZIP zwykle wymaga wykonania tych czynności:
Kotlin
// Get a ZipResourceFile representing a merger of both the main and patch files val expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Java
// Get a ZipResourceFile representing a merger of both the main and patch files ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
Powyższy kod zapewnia dostęp do dowolnego pliku znajdującego się w głównym pliku rozszerzającym lub pliku rozszerzającym z poprawką, odczytując go z połączonej mapy wszystkich plików z obu plików. W przypadku metody getAPKExpansionFile() wystarczy podać android.content.Context aplikacji oraz numer wersji głównego pliku rozszerzającego i pliku rozszerzającego z poprawką.
Jeśli wolisz odczytywać dane z określonego pliku rozszerzenia, możesz użyć konstruktora ZipResourceFile ze ścieżką do wybranego pliku rozszerzenia:
Kotlin
// Get a ZipResourceFile representing a specific expansion file val expansionFile = ZipResourceFile(filePathToMyZip) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Java
// Get a ZipResourceFile representing a specific expansion file ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
Więcej informacji o używaniu tej biblioteki w przypadku plików rozszerzeń znajdziesz w klasie SampleDownloaderActivity w aplikacji przykładowej. Zawiera ona dodatkowy kod do weryfikowania pobranych plików za pomocą CRC. Pamiętaj, że jeśli używasz tego przykładu jako podstawy własnej implementacji, musisz zadeklarować rozmiar w bajtach plików rozszerzenia w tablicy xAPKS.
Testowanie plików rozszerzeń
Zanim opublikujesz aplikację, musisz przetestować 2 rzeczy: odczytywanie plików rozszerzających i pobieranie plików.
Testowanie odczytu plików
Zanim prześlesz aplikację do Google Play, przetestuj jej możliwość odczytywania plików z pamięci współdzielonej. Wystarczy, że dodasz pliki do odpowiedniej lokalizacji w pamięci współdzielonej urządzenia i uruchomisz aplikację:
- Na urządzeniu utwórz odpowiedni katalog w pamięci współdzielonej, w którym Google Play będzie zapisywać pliki.
Jeśli na przykład nazwa pakietu to
com.example.android, musisz utworzyć katalogAndroid/obb/com.example.android/w przestrzeni pamięci współdzielonej. (Podłącz urządzenie testowe do komputera, aby zamontować pamięć współdzieloną i ręcznie utworzyć ten katalog). - Ręcznie dodaj pliki rozszerzenia do tego katalogu. Pamiętaj, aby zmienić nazwy plików tak, aby były zgodne z formatem nazw plików, którego będzie używać Google Play.
Na przykład niezależnie od typu pliku główny plik rozszerzający aplikacji
com.example.androidpowinien mieć nazwęmain.0300110.com.example.android.obb. Kod wersji może mieć dowolną wartość. Pamiętaj:- Główny plik rozszerzający zawsze zaczyna się od
main, a plik z poprawką odpatch. - Nazwa pakietu zawsze odpowiada nazwie pliku APK, do którego plik jest dołączony w Google Play.
- Główny plik rozszerzający zawsze zaczyna się od
- Gdy pliki rozszerzające znajdą się już na urządzeniu, możesz zainstalować i uruchomić aplikację, aby przetestować te pliki.
Oto kilka przypomnień dotyczących obsługi plików rozszerzeń:
- Nie usuwaj ani nie zmieniaj nazwy plików rozszerzających
.obb(nawet jeśli rozpakujesz dane w innym miejscu). Spowoduje to wielokrotne pobieranie pliku rozszerzającego przez Google Play (lub samą aplikację). - Nie zapisuj innych danych w
obb/katalogu. Jeśli musisz rozpakować niektóre dane, zapisz je w lokalizacji określonej przezgetExternalFilesDir().
Testowanie pobierania plików
Aplikacja musi czasami ręcznie pobrać pliki rozszerzające przy pierwszym otwarciu, dlatego ważne jest, aby przetestować ten proces i upewnić się, że aplikacja może prawidłowo wysyłać zapytania o adresy URL, pobierać pliki i zapisywać je na urządzeniu.
Aby przetestować wdrożenie w aplikacji procedury pobierania ręcznego, możesz opublikować ją na ścieżce testu wewnętrznego, dzięki czemu będzie dostępna tylko dla autoryzowanych testerów. Jeśli wszystko działa zgodnie z oczekiwaniami, aplikacja powinna rozpocząć pobieranie plików rozszerzających od razu po uruchomieniu głównej aktywności.
Uwaga: wcześniej można było testować aplikację, przesyłając nieopublikowaną wersję „roboczą”. Ta funkcja nie jest już obsługiwana. Zamiast tego musisz opublikować ją na ścieżce testów wewnętrznych, zamkniętych lub otwartych. Więcej informacji znajdziesz w artykule Wersje robocze aplikacji nie są już obsługiwane.
Aktualizowanie aplikacji
Jedną z największych zalet korzystania z plików rozszerzeń w Google Play jest możliwość aktualizowania aplikacji bez ponownego pobierania wszystkich oryginalnych zasobów. Google Play umożliwia dołączenie do każdego pakietu APK 2 plików rozszerzenia, więc możesz użyć drugiego pliku jako „łatki” zawierającej aktualizacje i nowe zasoby. Dzięki temu nie trzeba ponownie pobierać głównego pliku rozszerzającego, który może być duży i kosztowny dla użytkowników.
Plik rozszerzający z poprawką jest technicznie taki sam jak główny plik rozszerzający. System Android ani Google Play nie przeprowadzają faktycznego łączenia głównego pliku rozszerzającego z plikiem rozszerzającym z poprawką. Aplikacja musi samodzielnie wprowadzać niezbędne poprawki.
Jeśli używasz plików ZIP jako plików rozszerzających, biblioteka APK Expansion Zip dołączona do pakietu rozszerzeń APK umożliwia scalanie pliku z poprawką z głównym plikiem rozszerzającym.
Uwaga: nawet jeśli musisz wprowadzić zmiany tylko w pliku rozszerzenia z łatką, musisz zaktualizować plik APK, aby Google Play mógł przeprowadzić aktualizację.
Jeśli nie musisz wprowadzać zmian w kodzie aplikacji, po prostu zaktualizuj versionCode w pliku manifestu.
Jeśli nie zmienisz głównego pliku rozszerzającego powiązanego z plikiem APK w Konsoli Play, użytkownicy, którzy wcześniej zainstalowali Twoją aplikację, nie pobiorą głównego pliku rozszerzającego. Dotychczasowi użytkownicy otrzymają tylko zaktualizowany plik APK i nowy plik rozszerzający z łatką (zachowując poprzedni główny plik rozszerzający).
Oto kilka kwestii, o których warto pamiętać w związku z aktualizacjami plików rozszerzeń:
- Aplikacja może mieć tylko 2 pliki rozszerzające. jeden główny plik rozszerzający i jeden plik rozszerzający z poprawką. Podczas aktualizacji pliku Google Play usuwa poprzednią wersję (podobnie musi postępować Twoja aplikacja podczas ręcznych aktualizacji).
- Podczas dodawania pliku rozszerzającego z poprawką system Android nie wprowadza w rzeczywistości żadnych zmian w aplikacji ani w głównym pliku rozszerzającym. Aplikacja musi być zaprojektowana tak, aby obsługiwać dane dotyczące aktualizacji. Pakiet rozszerzenia APK zawiera jednak bibliotekę do używania plików ZIP jako plików rozszerzenia, która łączy dane z pliku poprawki z głównym plikiem rozszerzenia, dzięki czemu możesz łatwo odczytać wszystkie dane z pliku rozszerzenia.