以编程方式安装/卸载 APK(PackageManager 与 Intents)
我的应用程序安装其他应用程序,并且它需要跟踪它已安装的应用程序。当然,这可以通过简单地保留已安装应用程序的列表来实现。但这应该是没有必要的! 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);
使用Intent卸载APK:
Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);
这显然不是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);
使用 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);
差异
使用意图时,本地包管理器不会知道安装源自哪个应用程序。具体来说,getInstallerPackageName(...) 返回 null。
隐藏方法 installPackage(...) 将安装程序包名称作为参数,并且很可能能够设置此值。
隐藏
问题
是否可以使用意图指定包安装程序名称? (也许安装程序包的名称可以作为额外的内容添加到安装意图中?)
提示:如果您想下载 Android 源代码,您可以按照此处描述的步骤操作:下载源树。要提取 *.java 文件并根据包层次结构将它们放入文件夹中,您可以查看这个简洁的脚本: 在 Eclipse 中查看 Android 源代码。
My application installs other applications, and it needs to keep track of what applications it has installed. Of course, this could be achieved by simply keeping a list of installed applications. But this should not be necessary! It should be the responsibility of the PackageManager to maintain the installedBy(a, b) relationship. In fact, according to the API it is:
public abstract String getInstallerPackageName(String packageName) -
Retrieve the package name of the application that installed a package. This identifies which market the package came from.
The current approach
Install APK using Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);
Uninstall APK using Intent:
Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);
This is obviously not the way e.g. Android Market installs / uninstalls packages. They use a richer version of the PackageManager. This can bee seen by downloading the Android source code from the Android Git repository. Below are the two hidden methods that corresponds to the Intent approach. Unfortunately they are not available to external developers. But perhaps they will be in the future?
The better approach
Installing APK using the 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);
Uninstalling APK using the 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);
Differences
When using intents the local package manager is not made aware of which application the installation originated from. Specifically, getInstallerPackageName(...) returns null.
The hidden method installPackage(...) takes the installer package name as a parameter, and is most likely capable of setting this value.
Question
Is it possible to specify package installer name using intents?
(Maybe the name of the installer package can be added as an extra to the installation intent?)
Tip: If you want to download the Android source code you can follow the steps described here: Downloading the Source Tree. To extract the *.java files and put them in folders according to the package hierarchy you can check out this neat script: View Android Source Code in Eclipse.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
Android P+需要AndroidManifest.xml中的此权限
然后:
卸载。看起来更容易...
Android P+ requires this permission in AndroidManifest.xml
Then:
to uninstall. Seems easier...
目前第三方应用程序无法使用此功能。请注意,即使使用反射或其他技巧来访问 installPackage() 也无济于事,因为只有系统应用程序可以使用它。 (这是因为它是低级安装机制,在用户批准权限之后,因此常规应用程序访问它是不安全的。)
而且 installPackage() 函数参数在平台版本之间经常发生变化,因此您尝试访问它所做的任何操作都会在该平台的各种其他版本上失败。
编辑:
还值得指出的是,这个 installerPackage 最近才添加到平台(2.2?),最初实际上并不用于跟踪谁安装了该应用程序 - 平台使用它来确定在报告时启动谁应用程序的错误,用于实现 Android 反馈。 (这也是 API 方法参数发生变化的一次。)至少在它推出后的很长一段时间内,Market 仍然没有使用它来跟踪它已安装的应用程序(而且很可能仍然没有使用它) ),但只是使用它来将 Android 反馈应用程序(与市场分离)设置为“所有者”来处理反馈。
This is not currently available to third party applications. Note that even using reflection or other tricks to access installPackage() will not help, because only system applications can use it. (This is because it is the low-level install mechanism, after the permissions have been approved by the user, so it is not safe for regular applications to have access to.)
Also the installPackage() function arguments have often changed between platform releases, so anything you do trying access it will fail on various other versions of the platform.
EDIT:
Also it is worth pointing out that this installerPackage was only added fairly recently to the platform (2.2?) and was originally not actually used for tracking who installed the app -- it is used by the platform to determine who to launch when reporting bugs with the app, for implementing Android Feedback. (This was also one of the times the API method arguments changed.) For at least a long while after it was introduced, Market still didn't use it to track the apps it has installed (and it may very well still not use it), but instead just used this to set the Android Feedback app (which was separate from Market) as the "owner" to take care of feedback.
API 级别 14 引入了两个新操作:ACTION_INSTALL_PACKAGE 和 ACTION_UNINSTALL_PACKAGE。这些操作允许您传递 EXTRA_RETURN_RESULT 布尔值 extra 来获取(卸载)安装结果通知。
调用卸载对话框的示例代码:
并在 Activity#onActivityResult 方法:
API level 14 introduced two new actions: ACTION_INSTALL_PACKAGE and ACTION_UNINSTALL_PACKAGE. Those actions allow you to pass EXTRA_RETURN_RESULT boolean extra to get an (un)installation result notification.
Example code for invoking the uninstall dialog:
And receive the notification in your Activity#onActivityResult method:
如果您拥有设备所有者(或配置文件所有者,我没有尝试过)权限,您可以使用设备所有者 API 静默安装/卸载软件包。
用于卸载:
和安装包:
If you have Device Owner (or profile owner, I haven't tried) permission you can silently install/uninstall packages using device owner API.
for uninstalling:
and to install package:
访问这些方法的唯一方法是通过反射。您可以通过调用 getApplicationContext().getPackageManager() 并使用反射访问这些方法来获取 PackageManager 对象的句柄。查看这个 教程。
The only way to access those methods is through reflection. You can get a handle on a
PackageManager
object by callinggetApplicationContext().getPackageManager()
and using reflection access these methods. Checkout this tutorial.根据Froyo源代码,在PackageInstallerActivity中查询Intent.EXTRA_INSTALLER_PACKAGE_NAME额外键来获取安装程序包名称。
According to Froyo source code, the Intent.EXTRA_INSTALLER_PACKAGE_NAME extra key is queried for the installer package name in the PackageInstallerActivity.
如果您将包名称作为参数传递给任何用户定义的函数,请使用以下代码:
If you are passing package name as parameter to any of your user defined function then use the below code :
在获得 root 权限的设备上,您可以使用:
Util.sudo()
在此定义。On a rooted device, you might use:
Util.sudo()
is defined here.如果您使用的是 Kotlin、API 14+,并且只想显示应用程序的卸载对话框:
如果您想提示用户卸载其他应用程序,则可以将
packageName
更改为任何其他包名称该设备If you're using Kotlin, API 14+, and just wish to show uninstall dialog for your app:
You can change
packageName
to any other package name if you want to prompt the user to uninstall another app on the device先决条件:
您的 APK 需要由系统签名,正如前面正确指出的那样。实现这一目标的一种方法是自己构建 AOSP 映像并将源代码添加到构建中。
代码:
安装为系统应用程序后,您可以使用程序包管理器方法来安装和卸载 APK,如下所示:
安装:
卸载:
要在安装/卸载 APK 后进行回调,您可以使用这:
Prerequisite:
Your APK needs to be signed by system as correctly pointed out earlier. One way to achieve that is building the AOSP image yourself and adding the source code into the build.
Code:
Once installed as a system app, you can use the package manager methods to install and uninstall an APK as following:
Install:
Uninstall:
To have a callback once your APK is installed/uninstalled you can use this: