android 静默安装、卸载实现

摘要  android 静默安装、卸载实现

目录[-]

  • 1.系统安装程序
  • 2.监听系统发出的安装广播
  • 3.系统隐藏的api
  • 4.拷贝apk
  • 5.效果图
  • 6.补充说明
  • android签名机制(1)——了解签名
  • android中应用的安装卸载,大家(用android设备的)肯定不陌生。这里就来浅谈android应用的安装、卸载的实现方式。

    1.系统安装程序

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


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* 安装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(卸载)


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


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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里配置:



    ?
    1
    2
    3
    4
    5
    6
       < 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声明。调用的安装下载接口如下:


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

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

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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 静默安装、卸载实现_第1张图片
      (注:此处的包名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中报的异常的接口都注释掉即可


    • 实现回调接口,代码如下

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      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隐藏方法,代码如下:

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      /**
            * 静默安装
            * */
           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) {
                   
               }
       
           }

      卸载调用同上

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    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;
             String appName;
             String appId;
             String filename;
             String pkname;
             String type_name;
     
             public  MyPakcageInstallObserver(Context c, String appName,
                     String appId, String filename, String packagename,
                     String type_name) {
                 this .cxt = c;
                 this .appName = appName;
                 this .appId = appId;
                 this .filename = filename;
                 this .pkname = packagename;
                 this .type_name = type_name;
             }
     
             @Override
             public  void  packageInstalled(String packageName,  int  returnCode) {
                 MarketApplication ma = ((MarketApplication) cxt
                         .getApplicationContext());
                 Log.i(TAG,
                         "returnCode = "  + returnCode +  ","
                                 + ma.getApp_detail_status(appId)); // 返回1代表安装成功
                 Intent it =  new  Intent();
                 it.setAction(CustomAction.INSTALL_ACTION);
                 it.putExtra( "install_returnCode" , returnCode);
                 it.putExtra( "install_packageName" , packageName);
                 it.putExtra( "install_appName" , appName);
                 it.putExtra( "install_appId" , appId);
                 if  (returnCode ==  1 ) {
                     //ma.getAPPList();
                     //ma.setManagerLists();
                     if  (ma.getApp_detail_status(appId) == MarketApplication.APP_STATUS_UPDATITNG) {
                         ma.removeUpdateItem(pkname);
                         cxt.sendBroadcast(it);
                         return ;
                     }
     
                     SharedPreferences installedAPPInfo = cxt.getSharedPreferences(
                             "installedAPPInfo" , Context.MODE_WORLD_READABLE);
                     installedAPPInfo.edit().putString(packageName, appId).commit();
     
                     // 保存信息到数据库
                     if  (appId !=  null  && appName !=  null  && pkname !=  null
                             && type_name !=  null ) {
                         ContentResolver conResolver = cxt.getContentResolver();
                         ContentValues values =  new  ContentValues();
                         values.put(InstalledAppInfo.APP_ID, appId);
                         values.put(InstalledAppInfo.APP_NAME, appName);
                         values.put(InstalledAppInfo.APP_PKNAME, pkname);
                         values.put(InstalledAppInfo.APP_TYPENAME, type_name);
                         Uri result = conResolver.insert(
                                 InstalledAppInfo.CONTENT_URI, values);
                         Log.i(TAG,
                                 "#########install suscess...result:"
                                         + result.toString());
                     }
                     ma.setApp_detail_status(appId,
                             MarketApplication.APP_STATUS_INSTALLED);
                
                 File f =  new  File(filename);
                 if  (f.exists()) {
                     f.delete();
                 }
     
                 cxt.sendBroadcast(it);
             }
         }
     
         /**
          * sd卡不存在
          */
         public  static  final  int  NO_SDCARD = - 1 ;
     
         /**
          * 移动应用到SD Card
         
          * @param context
          * @param pkname
          * @return
          */
         public  static 

    你可能感兴趣的:(android,安装,卸载)