Android中实现静态的默认安装和卸载应用
转载自: http://blog.csdn.net/jiangwei0910410003/article/details/36427963
最近好长时间都没有写blog了,主要是因为最近工作上的事以及下载Android源码的事耽误的(下载源码这件事会在后续的blog中写道,这个真的很有意义呀~~),那么今天来写点什么呢?主要的灵感来自于早上看新闻看到一篇文章说有一款应用在后台中卸载用户
手机中的所有浏览器的app,不会被用户察觉,但是最后百度浏览器还是用反侦察技术找到这个邪恶的应用然后将其告上法庭了。那么我们就来看看怎么能够实现应用的静态安装和卸载呢?就是不让用户知道,下面就来一步一步的介绍一下实现步骤:
1.系统安装程序
android自带了一个安装程序---/system/app/PackageInstaller.apk.大多数情况下,我们手机上安装应用都是通过这个apk来安装
的。代码使用也非常简单:
[java] view plain copy- /* 安装apk */
- public static void installApk(Context context, String fileName) {
- Intent intent = new Intent();
- intent.setAction(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(Uri.parse("file://" + fileName),"application/vnd.android.package-archive");
- context.startActivity(intent);
- }
- /* 卸载apk */
- public static void uninstallApk(Context context, String packageName) {
- Uri uri = Uri.parse("package:" + packageName);
- Intent intent = new Intent(Intent.ACTION_DELETE, uri);
- context.startActivity(intent);
- }
通过发一个Intent,把应用所在的路径封装整uri.之后默认启动了PackageInstaller.apk来安装程序了。
但是此种情况下,仅仅是个demo而已,很难达到开发者的需求。如:
1).界面不好
2).被用户知晓
3).什么时候安装完了,卸载完了呢?
当然这里关于第三点的话,为了达到自己的需求,相信很多人都会接着来监听系统安装卸载的广播,继续接下来的代码逻辑。
监听系统发出的安装广播
在安装和卸载完后,android系统会发一个广播
android.intent.action.PACKAGE_ADDED(安装)
android.intent.action.PACKAGE_REMOVED(卸载)
咱们就监听这广播,来做响应的逻辑处理。实现代码:
[java] view plain copy- public class MonitorSysReceiver extends BroadcastReceiver{
- @Override
- public void onReceive(Context context, Intent intent){
- //接收安装广播
- if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {
- //TODO
- }
- //接收卸载广播
- if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) {
- //TODO
- }
- }
- }
[html] view plain copy
- <receiver android:name=".MonitorSysReceiver">
- <intent-filter>
- <action android:name="android.intent.action.PACKAGE_ADDED" />
- <action android:name="android.intent.action.PACKAGE_REMOVED" />
- </intent-filter>
- </receiver>
到此,确实安装卸载的整体流程都知道了,但是这个效果肯定是无法达到项目的需求。
一般这种应用商店类(豌豆荚)的项目,肯定是会要自定义提示框效果的安装卸载功能,而不是调用系统的安装程序。
那咱就要想法子实现静默安装、卸载咯。
下面这种调用系统隐藏api接口来实现静默安装卸载,是比较大众靠谱的,实现自定义的提示界面
(关于这个调用系统隐藏的api接口,我们在之前讲到如何获取手机电量的一篇文章中介绍过了
http://blog.csdn.net/jiangwei0910410003/article/details/25994337)
2.系统隐藏的api
隐藏api,顾名思义,普通情况下肯定是调用不到的。翻翻源码frameworksasecorejavaandroidcontentpm目录下
PackageManager.java,应该发现在注释行里有加上@hide声明。调用的安装下载接口如下:
安装接口:
[java] view plain copy- public abstract void installPackage(Uri packageURI,IPackageInstallObserver observer, int flags,String installerPackageName);
卸载接口:
[java] view plain copy- public abstract void deletePackage(String packageName,IPackageDeleteObserver observer, int flags);
并且都是抽象方法,需要咱们实现。
看参数里IPackageInstallObserver observer一个aidl回调通知接口,当前目录中找到这接口:
[java] view plain copy- package android.content.pm;
- /**
- * API for installation callbacks from the Package Manager.
- * @hide
- */
- oneway interface IPackageInstallObserver {
- void packageInstalled(in String packageName, int returnCode);
- }
好吧,这里有现成的干货,咱拿过来直接用呗(当然如果没有源码的那就算了,那能实现的只是demo)。具体步骤:
从源码中拷贝要使用的aidl回调接口:IPackageInstallObserver.aidl、IPackageDeleteObserver.aidl当然完全可以拷贝整个pm目
录,这样就不会报错了。
作者项目里面用到了pm,所以把PackageManager.java以及涉及到的一些文件也拷贝过来了,不然eclipse报找不到PackageManager
对象。结构如下:
注:此处的包名android.content.pm一定要和源码目录结构一致,不然源码里编译会提示找不到aidl接口。一切朝源码编译看齐
此处有2种方式实现:
1.直接只取IPackageDeleteObserver.aidl和IPackagerInstallObserver.aidl、IPackageMoveObserver.aidl等要使用的接口,然后通过
bindService来和系统连接服务,然后直接调用接口即可(这种没有方式作者没试过,不过原理上来说应该是可行的,除非系统没有
这个Service实现这个接口。有需求的可以深究下)
2.作者此处的方法是直接拷贝了源码PackageManager.java等文件过来,不过靠过来之后eclipse会提示一些接口错误,但这里作者把
上面那几个.java文件都放空了,因为用不到,只是为了编译过才拷贝了那么多文件。最简单的就是直接拷贝4个文件即可:
PackageManager.java
IPackageDeleteObserver.aidl
IPackagerInstallObserver.aidl
IPackageMoveObserver.aidl
然后把PackageManager.java中报的异常的接口都注释掉即可
实现回调接口,代码如下:
安装的回调接口:
[java] view plain copy- /*静默安装回调*/
- class MyPakcageInstallObserver extends IPackageInstallObserver.Stub{
- @Override
- public void packageInstalled(String packageName, int returnCode) {
- if (returnCode == 1) {
- Log.e("DEMO","安装成功");
- new ToastThread(InstallActivity.this,"安装成功").start();
- }else{
- Log.e("DEMO","安装失败,返回码是:"+returnCode);
- new ToastThread(InstallActivity.this,"安装失败,返回码是:"+returnCode).start();
- }
- }
- }
安装的方法:
[java] view plain copy- String fileName = Environment.getExternalStorageDirectory() + File.separator + "baidu"+File.separator +"UC.apk";
- Uri uri = Uri.fromFile(new File(fileName));
- int installFlags = 0;
- PackageManager pm = getPackageManager();
- try {
- PackageInfo pi = pm.getPackageInfo("com.UCMobile",PackageManager.GET_UNINSTALLED_PACKAGES);
- if(pi != null) {
- installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- }
- } catch (NameNotFoundException e) {
- }
- MyPakcageInstallObserver observer = new MyPakcageInstallObserver();
- pm.installPackage(uri, observer, installFlags, "com.UCMobile");
从代码中可以看到我们想安装的是从SD卡中的baidu文件夹中的UC.apk
(所以在测试程序的时候需要将UC.apk拷贝到SD卡中的baidu文件夹中,或者你可以自定义一个文件的存放目录)
卸载原理是一样的
卸载的回调接口:
[java] view plain copy- /* 静默卸载回调 */
- class MyPackageDeleteObserver extends IPackageDeleteObserver.Stub {
- @Override
- public void packageDeleted(String packageName, int returnCode) {
- if (returnCode == 1) {
- Log.e("DEMO","卸载成功...");
- new ToastThread(InstallActivity.this,"卸载成功...").start();
- }else{
- Log.e("DEMO","卸载失败...返回码:"+returnCode);
- new ToastThread(InstallActivity.this,"卸载失败...返回码:"+returnCode).start();
- }
- }
- }
卸载的功能:
[java] view plain copy- PackageManager pm = InstallActivity.this.getPackageManager();
- IPackageDeleteObserver observer = new MyPackageDeleteObserver();
- pm.deletePackage("com.UCMobile", observer, 0);
这里我们一定要传一个包名。
自此,静默安装卸载代码实现。最后在AndroidManifast.xml中要注册权限和添加为系统用户组
[html] view plain copy- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.autoinstallpackage.demo"
- android:versionCode="1"
- android:versionName="1.0.19"
- android:sharedUserId="android.uid.system">
- <uses-sdk android:minSdkVersion="4"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.DELETE_PACKAGES" />
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- ...
- </manifest>
这里要注意的地方有两个:第一个必须要添加:
[html] view plain copy- android:sharedUserId="android.uid.system"
这个是将其应用注册成系统用户组中,如果不加这行代码的话,会出现报错,但是加了这行代码之后,如果还报错的话,重新clean一下项目就好了,这个是Eclipse的一个bug吧
还要添加安装和卸载的权限:
[html] view plain copy- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.DELETE_PACKAGES" />
这时候上面的工作都做完之后,那么我们就来用Eclipse运行一下吧:
好吧,貌似不是那么的顺利,出现一个安装错误,其实这个信息去百度一下之后会找到很多的相关资料,因为这个问题有很多人遇
到过,所以解决的方法也是很多的,就是需要将我们的应用签名成系统级的即可,那么我们该怎么办呢?
网上普遍的两种方法:
第一种是:得到platform.pem,platform.x509.pem,signapk.jar这三个文件进行签名:签名的命令很简单:
java -jar signapk.jar platform.x509.pem platform.pem 需要签名的apk 签名之后的apk
下图是我签名的操作:
- 上一篇:没有了
- 下一篇:没有了