android中应用的安装卸载,大家(用android设备的)肯定不陌生。这里就来浅谈android应用的安装、卸载的实现方式。


1.系统安装程序

android自带了一个安装程序—/system/app/PackageInstaller.apk.大多数情况下,我们手机上安装应用都是通过这个apk来安装的。代码使用也非常简单:

/* 安装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而已,很难达到开发者的需求。如:
 

  • 界面不好
  • 什么时候安装完了,卸载完了呢?

为了达到自己的需求,相信很多人都会接着来监听系统安装卸载的广播,继续接下来的代码逻辑。


2.监听系统发出的安装广播

在安装和卸载完后,android系统会发一个广播

  • android.intent.action.PACKAGE_ADDED(安装)
  • android.intent.action.PACKAGE_REMOVED(卸载)

咱们就监听这广播,来做响应的逻辑处理。实现代码:

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
        }
    }
}

AndroidMenifast.xml里配置:

 <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>

到此,确实安装卸载的整体流程都知道了,但是这个效果肯定是无法达到项目的需求。
一般这种应用商店类的项目,肯定是会要自定义提示框效果的安装卸载功能,而不是调用系统的安装程序。
那咱就要想法子实现静默安装、卸载咯。
网上有很多法子,如执行adb install 或pm install -r命令安装。但我想这并不可靠。记得之前有做过一个应用来执行linux命令,是通过RunTime来执行命令的。
后来发现其实并不靠谱,还不如直接用C代码来实现。
下面这种调用系统隐藏api接口来实现静默安装卸载,是比较大众靠谱的,实现自定义的提示界面。O(∩_∩)O~


3.系统隐藏的api

隐藏api,顾名思义,普通情况下肯定是调用不到的。翻翻源码\frameworks\base\core\java\android\content\pm目录下PackageManager.java,应该发现
在注释行里有加上@hide声明。调用的安装下载接口如下:

public abstract void installPackage(Uri packageURI,
            IPackageInstallObserver observer, int flags,
            String installerPackageName);
public abstract void deletePackage(String packageName,
 IPackageDeleteObserver observer, int flags);

并且都是抽象方法,需要咱们实现。 

看参数里IPackageInstallObserver  observer一个aidl回调通知接口,当前目录中找到这接口:

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目录,这样就不会报错了O(∩_∩)O~。
  • 作者项目里面用到了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中报的异常的接口都注释掉即可


  • 实现回调接口,代码如下
class MyPakcageInstallObserver extends IPackageInstallObserver.Stub {
        Context cxt;
        String appName;
        String filename;
        String pkname;
        public MyPakcageInstallObserver(Context c, String appName,
                 String filename,String packagename) {
            this.cxt = c;
            this.appName = appName;
            this.filename = filename;
            this.pkname = packagename;
        }
        @Override
        public void packageInstalled(String packageName, int returnCode) {
            Log.i(TAG, “returnCode = ” + returnCode);// 返回1代表安装成功
                        if (returnCode == 1) {
                            //TODO
                        }
            Intent it = new Intent();
            it.setAction(CustomAction.INSTALL_ACTION);
            it.putExtra(“install_returnCode”, returnCode);
            it.putExtra(“install_packageName”, packageName);
            it.putExtra(“install_appName”, appName); cxt.sendBroadcast(it);
        }
    }

卸载回调接口同上。

  • 调用PackageManager.java隐藏方法,代码如下:
/**
     * 静默安装
     * */
    public static void autoInstallApk(Context context, String fileName,
            String packageName, String APPName) {
        Log.d(TAG, “jing mo an zhuang:” + packageName + “,fileName:” + fileName);
        File file = new File(fileName);
        int installFlags = 0;
        if (!file.exists())
            return;
        installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
        if (hasSdcard()) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
        }
        PackageManager pm = context.getPackageManager();
        try {
            IPackageInstallObserver observer = new MyPakcageInstallObserver(
                    context, APPName, appId, fileName,packageName,type_name);
            Log.i(TAG, “########installFlags:” + installFlags+”packagename:”+packageName);
            pm.installPackage(Uri.fromFile(file), observer, installFlags,
                    packageName);
        } catch (Exception e) {
        }
    }

卸载调用同上

很多码友联系,这里经常出错,现整理参考代码如下(下面代码有些格式问题)

package cn.thear;
import java.io.File;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.os.RemoteException;
import android.util.Log;
public class ApkOperateManager {
    public static String TAG = “ApkOperateManager”;
    /***安装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);
    }
    /**
     * 静默安装
     * */
    public static void installApkDefaul(Context context, String fileName,
            String packageName, String APPName, String appId, String type_name) {
        Log.d(TAG, “jing mo an zhuang:” + packageName + “,fileName:” + fileName
                + “,type_name:” + type_name);
        File file = new File(fileName);
        int installFlags = 0;
        if (!file.exists())
            return;
        installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
        if (hasSdcard()) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
        }
        PackageManager pm = context.getPackageManager();
        // try {  try {
            IPackageInstallObserver observer = new MyPakcageInstallObserver(
                    context, APPName, appId, fileName, packageName, type_name);
            Log.i(TAG, “########installFlags:” + installFlags + “packagename:”
                    + packageName);
            pm.installPackage(Uri.fromFile(file), observer, installFlags,
                    packageName);
        } catch (Exception e) {
            ((MarketApplication) context).setApp_detail_status(appId,
                    MarketApplication.APP_STATUS_NOTEXIT);
        }
    }
    /* 静默卸载 */
    public static void uninstallApkDefaul(Context context, String action,
            String packageName) {
        PackageManager pm = context.getPackageManager();
        IPackageDeleteObserver observer = new MyPackageDeleteObserver(context,
                action, packageName);
        pm.deletePackage(packageName, observer, 0);
    }
    /* 静默卸载回调 */
             private static class MyPackageDeleteObserver extends
            IPackageDeleteObserver.Stub {
        Context cxt;
        String action;
        String pkname;
        public MyPackageDeleteObserver(Context c, String action, String pkname) {
            this.cxt = c;
            this.action = action;
            this.pkname = pkname;
        }
        @Override
public void packageDeleted(String packageName, int returnCode) {
            Log.d(TAG, “returnCode = ” + returnCode + “,action:” + action
                    + “packageName:” + packageName + “,pkname:” + pkname);// 返回1代表卸载成功
            if (returnCode == 1) {//TODO 以下是删除数据库记录,只做参考
                /*SharedPreferences installedAPPInfo = cxt.getSharedPreferences(
                        “installedAPPInfo”, Context.MODE_WORLD_READABLE);
                if (installedAPPInfo.contains(packageName)) {
                    String appId = installedAPPInfo.getString(packageName,
                            “no this appId”);
                    ((MarketApplication) cxt.getApplicationContext())
                            .setApp_detail_status(appId,
                                    MarketApplication.APP_STATUS_NOTEXIT);
                    installedAPPInfo.edit().remove(packageName).commit();
                    ContentResolver conResolver = cxt.getContentResolver();
                    conResolver.delete(InstalledAppInfo.CONTENT_URI,
                            InstalledAppInfo.APP_PKNAME + ” = ” + “‘” + pkname
                                    + “‘”, null);
                }
                MarketApplication ma = ((MarketApplication) cxt
                        .getApplicationContext());
                Log.e(TAG, “###packageDeleted###111size:”
                        + ma.getManagerLists().size());
                ma.removeManagerItem(pkname);
                ma.removeUpdateItem(pkname);
                Log.e(TAG, “##packageDeleted####22222size:”
                        + ma.getManagerLists().size());*/
            }
            Intent it = new Intent();
            it.setAction(action);
            it.putExtra(“uninstall_returnCode”, returnCode);
            cxt.sendBroadcast(it);
        }
    }
    /* 静默安装回调 */
    private static class MyPakcageInstallObserver extends
            IPackageInstallObserver.Stub {
        Context cxt;