安装 assets 中的apk ,兼容6.0权限 处理7.0的崩溃

好记性不如烂笔头

前期准备 要安装的apk放到assets文件,三款手机 系统4.4 6.0 7.0
关于assets文件知识可以参考这里
准备


步骤1 copy assets 文件到 存储目录
1.1 清单权限

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

1.2 调用copyAssets方法

 private void copyAssets(Context context, String filename) {
        AssetManager assetManager = context.getAssets();
        InputStream in;
        OutputStream out;
        try {
            in = assetManager.open(filename);
            String outFileName = Environment.getExternalStorageDirectory().
            getAbsolutePath();//保存到外部存储,大部分设备是sd卡根目录
            String copyName = System.currentTimeMillis() + "qq.apk";//copy后具体名称
            File outFile = new File(outFileName, copyName);
            out = new FileOutputStream(outFile);
            copyFile(in, out);
            in.close();
            out.flush();
            out.close();
        } catch (IOException e) {
            System.out.println("");
        }
    }

    private void copyFile(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
    }

copyAssets() 传入两个参数,一个是Context 另一个是 assets 中的文件名(这里传“qq.apk”)

1.3 运行结果(仅限6.0以下) 这里虽然调用.getExternalStorageDirectory()但是系统默认是集成的sd卡,也就是常见的 /storage/emulated/0 俗称 内部存储
结果如图

1.4 安装
修改 copyAssets 方法如下

 private void copyAssets(Context context, String filename) {
        AssetManager assetManager = context.getAssets();
        InputStream in;
        OutputStream out;
        try {
            in = assetManager.open(filename);
            String outFileName = Environment.getExternalStorageDirectory()
            .getAbsolutePath();//保存到外部存储,大部分设备是sd卡根目录
            String copyName = System.currentTimeMillis() + "qq.apk";//copy后具体名称
            File outFile = new File(outFileName, copyName);
            out = new FileOutputStream(outFile);
            copyFile(in, out);
            in.close();
            out.flush();
            out.close();
            //copy 完毕,直接安装
            File file = new File(outFileName, copyName);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
            context.startActivity(intent);
        } catch (IOException e) {
            System.out.println("");
        }
    }

1.5 运行结果
运行结果


步骤2 >6.0 系统权限问题

当以上代码在 >6.0 系统运行时,会遇到权限问题 /storage/emulated/0/1493862005311qq.apk: open failed: EACCES (Permission denied)

这是因为 6.0 系统引入了动态权限管理,针对此问题我们可以用三方的PermissionsDispatcher当然了,这里不对三方过多依赖,我们自己请求权限, 首先代码动态请求,然后在系统回调中处理逻辑

//请求权限
    private static final int READ_CONTACTS_REQUEST = 1;
    public void getPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            //发起请求获得用户许可,可以在此请求多个权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    READ_CONTACTS_REQUEST);
        } else {
            //权限许可过,直接拷贝
            copyAssets(MainActivity.this, "qq.apk");
        }
    }

    //系统方法,从requestPermissions()方法回调结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        //确保是我们的请求
        if (requestCode == READ_CONTACTS_REQUEST) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "写文件权限被授予", Toast.LENGTH_SHORT).show();
                copyAssets(MainActivity.this, "qq.apk");
            } else if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "写文件权限被拒绝", Toast.LENGTH_SHORT).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

这时我们不直接调用copyAssets 而是调用getPermission 方法,其流程大概如下

Created with Raphaël 2.1.0 开 始 查看权限是否赋予了? 直接拷贝 结束 请求权限 onRequestPermissionsResult结果 用户是否赋予权限? 直接拷贝 结束 用户没有给予权限逻辑 yes no yes no

具体情况如图,第一次运行会动态提示
结果如图


步骤3 7.0 的文件共享问题

以上代码运行在 7.0 设备上会报错 ,报错位置就是在安装时
File file = new File(outFileName, copyName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
context.startActivity(intent);

报错内容如下
file:///storage/emulated/0/1493799271025qq.apk exposed beyond app through Intent.getData()
针对这个问题最常用的就是 FileProvider ,这里也一样

3.1 FileProvider 清单配置

provider 作为四大组件之一,要在application节点下进行配置,就跟 activity 一样

     <provider
            android:name="android.support.v4.content.FileProvider"
            //这里是一个口令字符串,等会儿获取文件时要用,内容可以随意但前后必须一致
            android:authorities="com.nf.hp.assets.provider"
            android:grantUriPermissions="true"
            android:exported="false">
            <!--元数据-->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                //这里配置共享文件路径,provider_paths只是文件名,可任意取
                android:resource="@xml/provider_paths" />
       </provider>

3.2 创建路径配置文件
在 res 下创建 xml 文件夹 里面新建 provider_paths文件 注意与清单中名称一致
如图
provider_paths 内容如下

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path path="." name="." />
</paths>

其中 <external-path path="." name="." /> 对应getExternalStorageDirectory()下所有文件及其子文件可以对外共享

3.3 再次修改 copyAssets安装方法 如下

//copy 完毕,直接安装
 File file = new File(outFileName, copyName);
 Intent intent = new Intent(Intent.ACTION_VIEW);
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        //>7.0时 用 provider 共享
       intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri contentUri = FileProvider.getUriForFile(context, "com.nf.hp.assets.provider",//这里与provider清单中一致
            new File(outFileName, copyName));
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
 } else {
      intent.setDataAndType(Uri.fromFile(file),
        "application/vnd.android.package-archive");
 }
ontext.startActivity(intent);

备注

针对 >6.0 时权限问题,可能有些同学发现并没有文章中错误,其实除了手机系统,还与工程的配置有关 .若 compileSdkVersiontargetSdkVersion 都比较小 比如小于6.0(API23),那么即使系统是 6.0 也不会出现权限问题


水平有限,如果错误望大家不吝指正,谢谢

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值