Moja aplikacja instaluje inne aplikacje i musi śledzić, jakie aplikacje zainstalowała. Oczywiście można to osiągnąć, po prostu zachowując listę zainstalowanych aplikacji. Ale to nie powinno być konieczne! Za utrzymanie relacji installedBy (a, b) powinien odpowiadać pakiet PackageManager. W rzeczywistości według API jest to:
public abstract String getInstallerPackageName (String nazwa_pakietu) - pobierz nazwę pakietu aplikacji, która zainstalowała pakiet. Pozwala to zidentyfikować rynek, z którego pochodzi pakiet.
Obecne podejście
Zainstaluj APK przy użyciu Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);
Odinstaluj APK przy użyciu zamiaru:
Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);
Oczywiście nie jest to sposób, w jaki np. Android Market instaluje / odinstalowuje pakiety. Używają bogatszej wersji PackageManager. Można to zobaczyć, pobierając kod źródłowy Androida z repozytorium Android Git. Poniżej znajdują się dwie ukryte metody, które odpowiadają podejściu Intent. Niestety nie są one dostępne dla zewnętrznych programistów. Ale może będą w przyszłości?
Lepsze podejście
Instalowanie APK przy użyciu PackageManager
/**
* @hide
*
* Install a package. Since this may take a little while, the result will
* be posted back to the given observer. An installation will fail if the calling context
* lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
* package named in the package file's manifest is already installed, or if there's no space
* available on the device.
*
* @param packageURI The location of the package file to install. This can be a 'file:' or a
* 'content:' URI.
* @param observer An observer callback to get notified when the package installation is
* complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
* @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
* {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
* @param installerPackageName Optional package name of the application that is performing the
* installation. This identifies which market the package came from.
*/
public abstract void installPackage(
Uri packageURI, IPackageInstallObserver observer, int flags,
String installerPackageName);
Odinstalowywanie APK przy użyciu PackageManager
/**
* Attempts to delete a package. Since this may take a little while, the result will
* be posted back to the given observer. A deletion will fail if the calling context
* lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
* named package cannot be found, or if the named package is a "system package".
* (TODO: include pointer to documentation on "system packages")
*
* @param packageName The name of the package to delete
* @param observer An observer callback to get notified when the package deletion is
* complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
* @param flags - possible values: {@link #DONT_DELETE_DATA}
*
* @hide
*/
public abstract void deletePackage(
String packageName, IPackageDeleteObserver observer, int flags);
Różnice
Podczas używania intencji lokalny menedżer pakietów nie jest informowany, z której aplikacji pochodzi instalacja. W szczególności getInstallerPackageName (...) zwraca null.
Ukryta metoda installPackage (...) przyjmuje nazwę pakietu instalatora jako parametr i najprawdopodobniej jest w stanie ustawić tę wartość.
Pytanie
Czy można określić nazwę instalatora pakietu za pomocą intencji? (Może nazwę pakietu instalatora można dodać jako dodatek do zamiaru instalacji?)
Wskazówka: jeśli chcesz pobrać kod źródłowy Androida, możesz wykonać kroki opisane tutaj: Pobieranie drzewa źródłowego. Aby wyodrębnić pliki * .java i umieścić je w folderach zgodnie z hierarchią pakietów, możesz sprawdzić ten zgrabny skrypt: Wyświetl kod źródłowy Androida w Eclipse .
Odpowiedzi:
Obecnie nie jest to dostępne dla aplikacji innych firm. Zauważ, że nawet użycie refleksji lub innych sztuczek w celu uzyskania dostępu do installPackage () nie pomoże, ponieważ tylko aplikacje systemowe mogą go używać. (Dzieje się tak, ponieważ jest to mechanizm instalacji niskopoziomowej, po zatwierdzeniu uprawnień przez użytkownika, więc zwykłe aplikacje nie mają do niego dostępu).
Również argumenty funkcji installPackage () często zmieniały się między wydaniami platformy, więc wszystko, co zrobisz, próbując uzyskać dostęp, zakończy się niepowodzeniem w różnych innych wersjach platformy.
EDYTOWAĆ:
Warto również zwrócić uwagę, że ten pakiet instalacyjny został dodany stosunkowo niedawno do platformy (2.2?) I pierwotnie nie był używany do śledzenia, kto zainstalował aplikację - jest używany przez platformę do określenia, kogo uruchomić podczas zgłaszania błędów aplikacja do wdrażania funkcji Android Feedback. (Był to również jeden z przypadków zmiany argumentów metody API). Przez co najmniej długi czas po jej wprowadzeniu Market nadal nie używał jej do śledzenia zainstalowanych aplikacji (i może nadal jej nie używać. ), ale zamiast tego po prostu ustawił aplikację Android Feedback (która była niezależna od Market) jako „właściciela” zajmującego się opiniami.
źródło
Android P + wymaga tego uprawnienia w AndroidManifest.xml
Następnie:
odinstalować. Wydaje się łatwiejsze ...
źródło
onDestroy()
metodzie?Interfejs API poziomu 14 wprowadził dwie nowe akcje: ACTION_INSTALL_PACKAGE i ACTION_UNINSTALL_PACKAGE . Te akcje pozwalają przekazać dodatkowe wartości logiczne EXTRA_RETURN_RESULT, aby otrzymać powiadomienie o (nie) instalacji.
Przykładowy kod wywołujący okno dezinstalacji:
I odbierz powiadomienie w metodzie Activity # onActivityResult :
źródło
ACTION_INSTALL_PACKAGE
będzie wymagałoREQUEST_INSTALL_PACKAGES
uprawnień na poziomie podpisu . Podobnie, począwszy od API 28 (Android P), wywołanieACTION_UNINSTALL_PACKAGE
będzie wymagałoREQUEST_DELETE_PACKAGES
zezwolenia nieszkodliwego . Przynajmniej według docs.Jeśli masz uprawnienia właściciela urządzenia (lub właściciela profilu, nie próbowałem), możesz po cichu instalować / odinstalowywać pakiety za pomocą interfejsu API właściciela urządzenia.
do odinstalowania:
i aby zainstalować pakiet:
źródło
Jedynym sposobem uzyskania dostępu do tych metod jest refleksja. Możesz uzyskać uchwyt do
PackageManager
obiektu, wywołującgetApplicationContext().getPackageManager()
i używając odbicia dostępu do tych metod. Sprawdź ten samouczek.źródło
Zgodnie z kodem źródłowym Froyo, dodatkowy klucz Intent.EXTRA_INSTALLER_PACKAGE_NAME jest odpytywany o nazwę pakietu instalatora w PackageInstallerActivity.
źródło
Na zrootowanym urządzeniu możesz użyć:
Util.sudo()
jest zdefiniowane tutaj.źródło
Jeśli przekazujesz nazwę pakietu jako parametr do dowolnej funkcji zdefiniowanej przez użytkownika, użyj poniższego kodu:
źródło
Jeśli używasz Kotlin, API 14+ i po prostu chcesz wyświetlić okno dezinstalacji swojej aplikacji:
Możesz zmienić
packageName
na dowolną inną nazwę pakietu, jeśli chcesz poprosić użytkownika o odinstalowanie innej aplikacji na urządzeniuźródło
Warunek wstępny:
Twój plik APK musi być podpisany przez system, jak poprawnie wskazano wcześniej. Jednym ze sposobów osiągnięcia tego jest samodzielne zbudowanie obrazu AOSP i dodanie kodu źródłowego do kompilacji.
Kod:
Po zainstalowaniu jako aplikacja systemowa możesz użyć metod menedżera pakietów, aby zainstalować i odinstalować pakiet APK w następujący sposób:
Zainstalować:
Odinstaluj:
Aby uzyskać oddzwonienie po zainstalowaniu / odinstalowaniu pakietu APK, możesz użyć tego:
źródło