APK'ları programlı olarak yükleme / kaldırma (PackageManager vs Intents)


142

Uygulamam diğer uygulamaları yüklüyor ve hangi uygulamaları yüklediğini takip etmesi gerekiyor. Tabii ki, bu sadece kurulu uygulamaların bir listesini tutarak elde edilebilir. Ama bu gerekli olmamalı! Kurulu (a, b) ilişkisini sürdürmek PackageManager'ın sorumluluğunda olmalıdır. Aslında, API'ye göre:

public abstract String getInstallerPackageName (String packageName) - Bir paketi yükleyen uygulamanın paket adını alır. Bu, paketin hangi pazardan geldiğini tanımlar.

Mevcut yaklaşım

Niyeti kullanarak APK yükle

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);

APK'yı Amaç kullanarak kaldırın:

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);

Bu, Android Market'in paketleri kurması / kaldırması gibi bir şey değildir. PackageManager'ın daha zengin bir sürümünü kullanırlar. Bu, Android Git deposundan Android kaynak kodunu indirerek görülebilir. Aşağıda, Niyet yaklaşımına karşılık gelen iki gizli yöntem bulunmaktadır. Ne yazık ki harici geliştiriciler tarafından kullanılamıyor. Ama belki de gelecekte olacaklar?

Daha iyi yaklaşım

PackageManager'ı kullanarak APK yükleme

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

APK'yı PackageManager kullanarak kaldırma

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

farklılıklar

  • Amaçları kullanırken, yerel paket yöneticisi yüklemenin hangi uygulamadan kaynaklandığı konusunda bilgilendirilmez. Özellikle, getInstallerPackageName (...) null değerini döndürür.

  • Gizli installPackage (...) yöntemi, yükleyici paketinin adını parametre olarak alır ve büyük olasılıkla bu değeri ayarlayabilir.

Soru

Amaçlarını kullanarak paket yükleyici adını belirtmek mümkün mü? (Belki de kurulum paketinin adı kurulum amacına ekstra olarak eklenebilir?)

İpucu: Android kaynak kodunu indirmek istiyorsanız, burada açıklanan adımları takip edebilirsiniz: Kaynak Ağacını İndirme. * .Java dosyalarını ayıklamak ve paket hiyerarşisine göre klasörlere koymak için bu düzgün komut dosyasına göz atabilirsiniz: Eclipse'de Android Kaynak Kodunu Görüntüle .


Metinde bazı URI'ler eksik. İzin verdiğim anda bunları ekleyeceğim (yeni kullanıcıların spam'ı önlemek için bazı kısıtlamaları var).
Håvard Geithus

1
kaldırma işlevini nasıl devre dışı bırakabilirim?

2
@ user938893: "kaldırma işlevini nasıl devre dışı bırakabilirim?" - Kaldırması zor kötü amaçlı yazılımlar üzerinde çalışıyoruz, değil mi?
Daniel

Yanıtlar:


66

Bu, şu anda üçüncü taraf uygulamaları tarafından kullanılamamaktadır. İnstallPackage () öğesine erişmek için yansıma veya başka hileler kullanmanın bile yardımcı olmayacağını unutmayın, çünkü yalnızca sistem uygulamaları kullanabilir. (Bunun nedeni, izinler kullanıcı tarafından onaylandıktan sonra düşük düzeyli yükleme mekanizması olmasıdır, bu nedenle normal uygulamaların erişime sahip olması güvenli değildir.)

Ayrıca installPackage () işlev argümanları genellikle platform sürümleri arasında değiştiğinden, erişmeye çalıştığınız her şey platformun diğer çeşitli sürümlerinde başarısız olacaktır.

DÜZENLE:

Ayrıca, bu yükleyicinin pakete sadece son zamanlarda oldukça yeni eklendiğini (2.2?) Ve aslında uygulamayı kimin yüklediğini izlemek için kullanılmadığını belirtmek gerekir - platform tarafından hataları bildirirken kimin başlatılacağını belirlemek için kullanılır Android Geri Bildirim uygulaması için (Bu aynı zamanda API yöntemi argümanlarının değiştiği zamanlardan biriydi.) Piyasaya sürüldükten sonra en azından uzun bir süre boyunca, Market hala yüklediği uygulamaları izlemek için kullanmadı (ve yine de çok iyi kullanmayabilir) ), ancak bunun yerine, geri bildirimle ilgilenmek için Android Geri Bildirim uygulamasını (Market'ten ayrı) "sahip" olarak ayarlamak için kullanılır.


"İnstallPackage () işlevine erişmek için yansıma veya başka hileler kullanmanın bile yardımcı olmayacağını unutmayın, çünkü yalnızca sistem uygulamaları kullanabilir." Yerli Android'in kendisi dışında belirli bir platform için bir paket yükleme / kaldırma / yönetme uygulaması yaptığımı varsayalım. Yükleme / kaldırma işlemine nasıl erişmeliyim?
dascandy

uygun şekilde oluşturulmuş bir Niyet ile startActivity (). (Bunun StackOverflow üzerinde başka bir yerde yanıtlandığından eminim, bu yüzden burada yanlış bir şey alma riski altında kesin cevabı vermeye çalışmayacağım.)
hackbod

mmmkay, standart Android yükleme / kaldırma iletişim kutularını getirir. Bu ayrıntılar zaten ele alındı ​​- "sadece **** bu paketi yükle" ve "sadece **** bu paketi kaldır" işlevlerini arıyorum, kelimenin tam anlamıyla soru sorulmadı.
dascandy

2
Dediğim gibi, bunlar üçüncü taraf uygulamaları tarafından kullanılamaz. Kendi sistem görüntünüzü oluşturuyorsanız, platform uygulamasına sahipsiniz ve işlevleri burada bulabilirsiniz, ancak bunlar normal üçüncü taraf uygulamalarının kullanabileceği API'lerin bir parçası değildir.
hackbod

yükleme, kaldırma ve yedekleme işlevselliği ile apk dosya gezgini yapıyorum, bu yüzden google bana uygulamaumu google play'de yayınlamada kalmamı sağlıyor mu? ve hangi politikayı kıracağız?
Rahul Mandaliya

86

Android P + için AndroidManifest.xml dosyasında bu izin gereklidir

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

Sonra:

Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:com.example.mypackage"));
startActivity(intent);

kaldırmak için. Daha kolay görünüyor ...


Bu kodu çalıştıran uygulama olabilir mi? onDestroy()yöntem gibi ?
Mehdi-Malv

ACTION_INSTALL_PACKAGE hakkında ne dersiniz? Uygulamamızı oyun mağazasından en son sürümü indirip yükleyebilir miyiz?
MAS. John

3
Eğer "ACTION_DELETE" veya "ACTION_UNINSTALL_PACKAGE" kullanırsanız Android P uygulamaları silerek tezahür izni "android.permission.REQUEST_DELETE_PACKAGES" olursa olsun gerektirdiğinden developer.android.com/reference/android/content/...
Darklord5

Android P izninden bahsettiğiniz için teşekkür ederim, takıldım ve daha önce neler olduğundan emin değildim.
Avi Parshan

43

API düzey 14 iki yeni işlem başlattı : ACTION_INSTALL_PACKAGE ve ACTION_UNINSTALL_PACKAGE . Bu eylemler , (un) kurulum sonucu bildirimi almak için EXTRA_RETURN_RESULT boolean extra öğesini iletmenize izin verir .

Kaldırma iletişim kutusunu çağırmak için örnek kod:

String app_pkg_name = "com.example.app";
int UNINSTALL_REQUEST_CODE = 1;

Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);  
intent.setData(Uri.parse("package:" + app_pkg_name));  
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, UNINSTALL_REQUEST_CODE);

Ayrıca, Etkinlik # onActivityResult yönteminizde bildirimi alın :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == UNINSTALL_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            Log.d("TAG", "onActivityResult: user accepted the (un)install");
        } else if (resultCode == RESULT_CANCELED) {
            Log.d("TAG", "onActivityResult: user canceled the (un)install");
        } else if (resultCode == RESULT_FIRST_USER) {
            Log.d("TAG", "onActivityResult: failed to (un)install");
        }
    }
}

Bu eylem iletişim kutusundan, kullanıcının ok tuşuna bastığını veya iptal ettiğini nasıl onaylayabilirim, böylece buna dayanarak karar alabilirim
Erum

2
@Erum İstediğinize bir örnek ekledim
Alex Lipov

Kurulumda, iptal düğmesi onActivityResult yöntemine bir sonuç
alamadı

2
API 25'ten başlayarak arama yapmak ACTION_INSTALL_PACKAGEiçin imza düzeyinde REQUEST_INSTALL_PACKAGESizin gerekir. Benzer şekilde, API 28'den (Android P) başlayarak arama yapmak ACTION_UNINSTALL_PACKAGEiçin tehlikeli olmayan REQUEST_DELETE_PACKAGESizin gerekir. En azından dokümanlara göre.
Steve Blackwell

22

Cihaz Sahibi (veya profil sahibim, denemedim) izniniz varsa, cihaz sahibi API'sını kullanarak paketleri sessiz bir şekilde kurabilir / kaldırabilirsiniz.

kaldırmak için:

public boolean uninstallPackage(Context context, String packageName) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        int sessionId = 0;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        packageInstaller.uninstall(packageName, PendingIntent.getBroadcast(context, sessionId,
                new Intent("android.intent.action.MAIN"), 0).getIntentSender());
        return true;
    }
    System.err.println("old sdk");
    return false;
}

ve paketi yüklemek için:

public boolean installPackage(Context context,
                                     String packageName, String packagePath) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        try {
            int sessionId = packageInstaller.createSession(params);
            PackageInstaller.Session session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(packageName + ".apk", 0, -1);
            readTo(packagePath, out); //read the apk content and write it to out
            session.fsync(out);
            out.close();
            System.out.println("installing...");
            session.commit(PendingIntent.getBroadcast(context, sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    System.err.println("old sdk");
    return false;
}

Bunu sahibi cihaz olarak yapmanın mümkün olduğunu biliyordum. Cevap için teşekkürler!
Luke Cauthen

@sandeep, APK'nın içeriğini çıktı akışına okuyor
Ohad Cohen

@LukeCauthen cihaz sahibi olarak denediniz mi? işe yaradı mı?
NetStarter

@NetStarter Evet var. Bu sadece cihaz sahibi olmak için bir uygulama almak eşek bir acıdır. Bunu yaptıktan sonra, normalde kök gerektiren çok fazla güç elde edersiniz.
Luke Cauthen

1
Kaldırma işleminin çalışması için manifestinize android.permission.DELETE_PACKAGES eklemeniz gerektiğini lütfen unutmayın (Api düzey 22 veya altı üzerinde test edilmiştir)
benchuk

4

Bu yöntemlere erişmenin tek yolu düşünmektir. Bir üzerinde yürütebilirim PackageManagerarayarak nesne getApplicationContext().getPackageManager()bu yöntemleri ve yansıma erişimini kullanarak. Ödemeye Bu öğretici.


Bu 2.2 ile harika çalışıyor, ama 2.3 ile kullanma şansım olmadı
Birisi

3
Yansıma tüm api sürümlerinde sabit değil
HandlerExploit

3

Froyo kaynak koduna göre, PackageInstallerActivity'deki yükleyici paket adı için Intent.EXTRA_INSTALLER_PACKAGE_NAME ekstra anahtarı sorgulandı.


1
Bu taahhüdüne bakarak işe
yaradığını

2

Köklü bir cihazda şunları kullanabilirsiniz:

String pkg = context.getPackageName();
String shellCmd = "rm -r /data/app/" + pkg + "*.apk\n"
                + "rm -r /data/data/" + pkg + "\n"
                // TODO remove data on the sd card
                + "sync\n"
                + "reboot\n";
Util.sudo(shellCmd);

Util.sudo() burada tanımlanır.


SD karta önceden indirilmiş bir uygulama kurmanın bir yolu var mı? Veya Android Platformunda kabukta hangi komutları kullanabileceğimizi kontrol etmek için bana bir sayfa önerebilir misiniz?
yahya

1
@yahya developer.android.com/tools/help/shell.html "pm android" ifadesi tarafından bulundu, pm = paket yöneticisi
18446744073709551615


Çok teşekkürler! Bu linkler başlamak için gerçekten harika rehberler :)
yahya

@ V.Kalyuzhnyu Eskiden 2015 yılında çalışırdı. IIRC bir Samsung Galaxy, belki S5'ti.
18446744073709551615

2

Paket adını kullanıcı tanımlı işlevinizden herhangi birine parametre olarak geçiriyorsanız, aşağıdaki kodu kullanın:

    Intent intent=new Intent(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+packageName));
    startActivity(intent);

0

Kotlin, API 14+ kullanıyorsanız ve uygulamanız için kaldırma iletişim kutusunu göstermek istiyorsanız:

startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply {
    data = Uri.parse("package:$packageName")
})

packageNameKullanıcının cihazdaki başka bir uygulamayı kaldırmasını istemek isterseniz başka bir paket adıyla değiştirebilirsiniz .


0

Önkoşul:

APK'nızın daha önce belirtildiği gibi sistem tarafından imzalanması gerekir. Bunu başarmanın bir yolu, AOSP görüntüsünü kendiniz oluşturmak ve kaynak kodunu yapıya eklemektir.

Kod:

Bir sistem uygulaması olarak yüklendikten sonra, aşağıdaki gibi bir APK yüklemek ve kaldırmak için paket yöneticisi yöntemlerini kullanabilirsiniz:

Yüklemek:

public boolean install(final String apkPath, final Context context) {
    Log.d(TAG, "Installing apk at " + apkPath);
    try {
        final Uri apkUri = Uri.fromFile(new File(apkPath));
        final String installerPackageName = "MyInstaller";
        context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

Kaldırma:

public boolean uninstall(final String packageName, final Context context) {
    Log.d(TAG, "Uninstalling package " + packageName);
    try {
        context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

APK'nız yüklendikten / kaldırıldıktan sonra geri arama yapmak için bunu kullanabilirsiniz:

/**
 * Callback after a package was installed be it success or failure.
 */
private class InstallObserver implements IPackageInstallObserver {

    @Override
    public void packageInstalled(String packageName, int returnCode) throws RemoteException {

        if (packageName != null) {
            Log.d(TAG, "Successfully installed package " + packageName);
            callback.onAppInstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to install package.");
            callback.onAppInstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback after a package was deleted be it success or failure.
 */
private class DeleteObserver implements IPackageDeleteObserver {

    @Override
    public void packageDeleted(String packageName, int returnCode) throws RemoteException {
        if (packageName != null) {
            Log.d(TAG, "Successfully uninstalled package " + packageName);
            callback.onAppUninstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to uninstall package.");
            callback.onAppUninstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback to give the flow back to the calling class.
 */
public interface InstallerCallback {
    void onAppInstalled(final boolean success, final String packageName);
    void onAppUninstalled(final boolean success, final String packageName);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.