Install Apk 兼容性问题

作者: Bear_android | 来源:发表于2018-11-08 15:08 被阅读26次

    问题描述

    现象

    代码执行安装Apk,出现系统弹框解析错误,解析包时出现错误

    场景

    在华为P20 Android 8.0 手机上,下载Apk并使用通知栏进度条显示,开启应用锁屏通知权限,下载过程在锁屏情况下进行,下载完成后自动执行安装Apk,在解锁后出现系统弹框,解析包出现错误。

    解决之前安装Apk的方法

    首先在AndroidManifest中声明fileProvider
      <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_path" />
            </provider>
    

    provider属性说明

    属性 说明
    name android V4 包中的类FileProvider
    authorities 你的文件的Uri的域名一般以包名.fileprovider的格式,防止重名
    exported 设置不允许导出,我们的FileProvider应该是私有的
    grantUriPermissions 允许获取文件的临时访问权限
    resourse 设置FileProvider访问的文件路径

    res包下创建file_path.xml

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path
            name="external_path"
            path="test" />
    
        <cache-path
            name="internal_path"
            path="test" />
            
            这里可以创建很多个paths,但是每个paths的name不能一样
            
    
    </paths>
    

    path 说明

      <files-path name="*name*" path="*path*" />   对应的是:Context.getFileDir()的路径地址
       
         对应路径:Context.getFileDir()+"/${path}/"
         得到路径:content://${applicationId}/&{name}/
    
         <cache-path name="*name*" path="*path*" />  
         对应路径:Context.getCacheFir()+"/${path}/"
         得到路径:content://${applicationId}/&{name}/
    
    
         <external-path name="*name*" path="*path*" />  
         对应路径:Environment.getExternalStorageDirectory()+"/${path}/"
         得到路径:content://${applicationId}/&{name}/
    
         <external-files-path name="*name*" path="*path*" />  
         对应路径:Context.getExternalStorageDirectory()+"/${path}/"
         得到路径:content://${applicationId}/&{name}/
    
         <external-cache-path name="*name*" path="*path*" />   
         对应路径: Context.getExternalCacheDir()+"/${path}/"
         得到路径:content://${applicationId}/&{name}/
    

    举个例子说明:

                            path做如下声明
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <files-path name="my_images" path="images/"/>
    </paths>
    
    File imagePath = new File(Context.getFilesDir(), "images");
    File newFile = new File(imagePath, "default_image.jpg");
    Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
    
    contentUri值为:content://com.mydomain.fileprovider/my_images/default_image.jpg
    
    安装apk的方法(7.0版本兼容问题)
     public static void installApk(Context context,File apkFile){
            try {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                Uri apkUri = null;
                //判断版本是否是 7.0 及 7.0 以上
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    apkUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
                    //添加对目标应用临时授权该Uri所代表的文件
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                } else {
                    apkUri = Uri.fromFile(apkFile);
                }
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setDataAndType(apkUri,
                        "application/vnd.android.package-archive");
                context.startActivity(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    
    Android 8.0系统需要声明权限
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGE" />
    

    OK,以上就是大家普遍解决7.0,以及8.0版本兼容问题的方法。

    但是,在上文描述的场景中依然报出了错误:

    java.lang.SecurityException: Permission Denial: 
    opening provider android.support.v4.content.FileProvider from ProcessRecord{cc3ad2316425:
    com.android.packageinstaller/u0a21} (pid=16425, uid=10021) that is not exported from uid 10340
    
    懵逼.jpg

    问题定位

    经过短暂的懵逼后,开始通过各种方式,探索问题的原因。

    根据系统log分析,猜测在锁屏时,用于安装Apk的service处于休眠或者不可用的状态,导致通过intent.addflags方式赋予的临时权限失效了。于是,再次仔细看了官方文档后,发现还有一个方法,可以生成权限且在主动调用方法或者手机重启后才会失效。

    改进后的代码

    public static void installApk(Context context,File apkFile){
            try {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                Uri apkUri = null;
                //判断版本是否是 7.0 及 7.0 以上
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    apkUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
                    //添加对目标应用临时授权该Uri所代表的文件
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                } else {
                    apkUri = Uri.fromFile(apkFile);
                }
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setDataAndType(apkUri,
                        "application/vnd.android.package-archive");
                //查询所有符合 intent 跳转目标应用类型的应用,注意此方法必须放置setDataAndType的方法之后
                        List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
                        //然后全部授权
                        for (ResolveInfo resolveInfo : resInfoList) {
                            String packageName = resolveInfo.activityInfo.packageName;
                            context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        }        
                context.startActivity(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    

    再次尝试,此问题再没有出现。

    相关文章

      网友评论

        本文标题:Install Apk 兼容性问题

        本文链接:https://www.haomeiwen.com/subject/huswxqtx.html