以编程方式安装/卸载APK(PackageManager vs Intents)

Håv*_*hus 137 android install uninstall android-intent apk

我的应用程序安装其他应用程序,它需要跟踪它已安装的应用程序.当然,这可以通过简单地保留已安装的应用程序列表来实现.但这不应该是必要的!PackageManager应该负责维护installedBy(a,b)关系.事实上,根据API,它是:

public abstract String getInstallerPackageName(String packageName) - 检索安装包的应用程序的包名称.这确定了包裹来自哪个市场.

目前的做法

使用Intent安装APK

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

使用Intent卸载APK:

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

这显然不是Android Market安装/卸载软件包的方式.他们使用更丰富的PackageManager版本.通过从Android Git存储库下载Android源代码可以看到这一点.以下是与Intent方法相对应的两种隐藏方法.不幸的是,外部开发人员无法使用它们.但也许他们将来会是这样?

更好的方法

使用PackageManager安装APK

/**
 * @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);
Run Code Online (Sandbox Code Playgroud)

使用PackageManager卸载APK

/**
 * 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);
Run Code Online (Sandbox Code Playgroud)

差异

  • 使用意图时,本地程序包管理器不知道安装源自哪个应用程序.具体来说,getInstallerPackageName(...)返回null.

  • 隐藏方法installPackage(...)将安装程序包名称作为参数,并且很可能能够设置此值.

是否可以使用意图指定包安装程序名称? (也许安装程序包的名称可以作为安装意图的额外添加?)

提示:如果要下载Android源代码,可以按照此处描述的步骤操作:下载源树.要提取*.java文件并根据包层次结构将它们放在文件夹中,您可以查看这个简洁的脚本:在Eclipse中查看Android源代码.

Joh*_*Tex 74

[卸载]

怎么样:

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
Run Code Online (Sandbox Code Playgroud)

用于卸载.似乎更容易......

  • 由于Android P删除应用程序需要清单权限“ android.permission.REQUEST_DELETE_PACKAGES”,无论您使用的是“ ACTION_DELETE”还是“ ACTION_UNINSTALL_PACKAGE” https://developer.android.com/reference/android/content/Intent.html#ACTION_UNINSTALL_PACKAGE (2认同)

hac*_*bod 66

目前第三方应用程序无法使用此功能.请注意,即使使用反射或其他技巧来访问installPackage()也无济于事,因为只有系统应用程序才能使用它.(这是因为它是低级别的安装机制,在用户批准权限之后,因此常规应用程序无法安全访问.)

此外,installPackage()函数参数经常在平台版本之间发生更改,因此您尝试访问它的任何内容都将在该平台的各种其他版本上失败.

编辑:

另外值得指出的是,这个installerPackage最近才刚刚添加到平台(2.2?),并且最初并没有用于跟踪安装应用程序的用户 - 平台使用它来确定在报告错误时启动谁该应用程序,用于实现Android反馈.(这也是API方法参数发生变化的次数之一.)至少在推出它之后很长一段时间,Market仍然没有使用它来跟踪它已经安装的应用程序(它可能仍然没有使用它),但只是用它来设置Android反馈应用程序(与市场分开)作为"所有者"来处理反馈.

  • 正如我所说,这些不适用于第三方应用程序.如果您正在制作自己的系统映像,则可以使用平台实现,并且可以在那里找到这些功能,但它们不是普通第三方应用程序可用的API的一部分. (2认同)

Pir*_*hah 38

API级别14引入了两个新操作:ACTION_INSTALL_PACKAGEACTION_UNINSTALL_PACKAGE.这些操作允许您传递EXTRA_RETURN_RESULT boolean extra以获取(un)安装结果通知.

调用卸载对话框的示例代码:

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);
Run Code Online (Sandbox Code Playgroud)

并在您的Activity#onActivityResult方法中接收通知:

@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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Erum我已经为你提出的问题添加了一个例子 (2认同)
  • 从API 25开始,调用`ACTION_INSTALL_PACKAGE`将需要签名级别[`REQUEST_INSTALL_PACKAGES]](https://developer.android.com/reference/android/Manifest.permission.html#REQUEST_INSTALL_PACKAGES)权限。同样,从API 28(Android P)开始,调用`ACTION_UNINSTALL_PACKAGE`将需要非危险的[`REQUEST_DELETE_PACKAGES]](https://developer.android.com/reference/android/Manifest.permission.html#REQUEST_DELETE_PACKAGES)权限。至少根据文档。 (2认同)

Oha*_*hen 22

如果您拥有设备所有者(或配置文件所有者,我尚未尝试过)权限,您可以使用设备所有者API静默安装/卸载软件包.

卸载:

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;
}
Run Code Online (Sandbox Code Playgroud)

并安装包:

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;
}
Run Code Online (Sandbox Code Playgroud)