Android7.0下载apk后安装出现解析包错误

Android7.0下载apk后安装出现解析包错误_第1张图片
5187235268b5493ee90307a133cc8d10.jpg

这个问题困扰了我整整一天时间,所以决定记录下来!
我要说的是这个错误其实并不是因为7.0私密文件夹权限引起的错误,因为AndroidN出了这么久,大家也都应该对FileProvider有所了解了。

1.下载

我采用的自然是简单的系统自带的DownloadManager方式来下载

 /**
     * 下载APK文件
     */
    private void downloadApk(String downloadUrl) {
        clearApk(getString(R.string.app_name_en)+".apk");
        //TODO 1.封装下载请求
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
        //TODO 2.下载标题,描述
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setTitle(getString(R.string.app_name_en));
        request.setDescription(getString(R.string.app_name_en)+"下载");
        // TODO 3.设置下载文件的保存位置
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,getString(R.string.app_name_en)+".apk");
        // TODO 4.获取下载管理器服务的实例, 添加下载任务
        DownloadManager manager = (DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE);
        manager.enqueue(request);// 将下载请求加入下载队列, 返回一个下载ID
    }

    public static File clearApk(String apkName) {
        File apkFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), apkName);
        if (apkFile.exists()) {
            apkFile.delete();
        }
        return apkFile;
    }
2.注册广播

            
                
                
                
            
        
3.广播通知下载完毕,并开始安装
public class DownloadManagerReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) {
            L.i("用户点击了通知");
            // 点击下载进度通知时, 对应的下载ID以数组的方式传递
            long[] ids = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
            L.i("ids: " + Arrays.toString(ids));
        } else if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
            /*
             * 获取下载完成对应的下载ID, 这里下载完成指的不是下载成功, 下载失败也算是下载完成,
             * 所以接收到下载完成广播后, 还需要根据 id 手动查询对应下载请求的成功与失败.
             */
            long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L);
            L.i("id: " + downloadId);
            // 根据获取到的ID,使用上面第3步的方法查询是否下载成功
            DownloadManager manager = (DownloadManager) MyApplication.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(downloadId);
            Cursor cursor = manager.query(query);
            if (!cursor.moveToFirst()) {
                cursor.close();
                return;
            }
            int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
            String localFilename;
            if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
                localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
            }else{
                localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
            }
            if (status == DownloadManager.STATUS_SUCCESSFUL) {
                T.showLong(context,"下载完成,点击通知栏安装!");
                System.out.println("下载成功, 打开文件, 文件路径: " + localFilename);
//                setPermission(localFilename);
                installApk(context,localFilename);
            }
        }
    }


    /**
     * 安装APK
     */
    private void installApk(Context context,String filePath) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        File file = new File(Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_DOWNLOADS,context.getString(R.string.app_name_en)+".apk");
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
//            File file = new File(context.getExternalFilesDir(null)+"/"+ Environment.DIRECTORY_DOWNLOADS,context.getString(R.string.app_name_en)+".apk");
            Uri apkUri= FileProvider.getUriForFile(context, "cq.cake.shopingmarket.three.fileprovider", file);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            L.i("要安装的apk路径为=="+apkUri.getPath());
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        }else{
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }

    /**
     * 提升读写权限
     * @param filePath 文件路径
     * @return
     * @throws IOException
     */
//    public static void setPermission(String filePath)  {
//        String command = "chmod " + "777" + " " + filePath;
//        Runtime runtime = Runtime.getRuntime();
//        try {
//            runtime.exec(command);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }

}

由于今天主要是说出现错误的原因,所以三步就先把正确的代码贴了出来。这个问题也困扰了我一整天。其实最后证实主要原因还是路径不正确导致的,为什么会犯这么低级的错误呢!因为我把这个解析包错误的原因看得太宽泛了。

1.修正一,百度了这个错误,有的人说打的包不正确,应该都是debug包或正式包,但我试了试问题依旧
2.修正二,有的人说是因为AndroidStudio打v1和v2包不兼容,所以有这个问题,我想说我一直都是用的v1的方式打包
3.修正三,有的人说你的apk就不完整,但是我点击通知栏下载好的,却可以安装,说明包没有问题
4.修正四,有的人说你的7.0的系统需要fileprovider的方式来访问,我想说如果不用FileProvider,来访问我的应用直接就死了,所以也不是这个原因
5.修正五,有的说是权限不够,Linux需要提权,所以我在使用FileProvider前,调用了如下方法来提升权限,发现问题依旧

 /**
     * 提升读写权限
     * @param filePath 文件路径
     * @return
     * @throws IOException
     */
//    public static void setPermission(String filePath)  {
//        String command = "chmod " + "777" + " " + filePath;
//        Runtime runtime = Runtime.getRuntime();
//        try {
//            runtime.exec(command);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }

6.修正六,最后只能确定是在路径上有问题,我就一步一步的打印路径,看看到底哪里有问题,下载的路径,广播后通知的路径,安装前的路径,最后发现安装的路径居然和广播的路径不同

Android7.0下载apk后安装出现解析包错误_第2张图片
修正.png

居然在安装的时候在前面多了一个download,这个是在file_paths里面的别名,意义就是隐藏掉前面的路径名称,最开始我以为是名称不是Download引起的,所以后面加个一个download的root-path,却发现这样毫无作用。

所以最关键的问题还是出现installApk这个方法里面,最开始我是通过广播得到的下载文件的路径和名称:

 String localFilename;
            if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
                localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
            }else{
                localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
            }

然后把这个路径名称传递进入installApk方法,但是在这个安装方法里面由于要适配7.0,所以使用了FileProvider来获取Uri,就导致了路径被变更了,但是却不知道如何来修改这个路径,毕竟广播过来的这个路径确实是正确的,只是因为使用了FileProvider而导致的路径不正确(如上图所示)。最后在群里面询问了遇到同样问题的同学,才知道他是自己拼的File:

        File file = new File(Environment.getExternalStorageDirectory()+"/"+Environment.DIRECTORY_DOWNLOADS,context.getString(R.string.app_name_en)+".apk");

这样拼接后果然出现了,期待已久的安装界面!到此这个问题总算解决了,所以广播后查询的下载目录localFilename也就失去了意思,也可以省略了!唯一有用的就是下载完成后是否真正成功的状态,因为下载完成并不代表下载成功,记录下来给遇到同样困惑的同学!

int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
                T.showLong(context,"下载完成,点击通知栏安装!");
                installApk(context);
            }

你可能感兴趣的:(Android7.0下载apk后安装出现解析包错误)