适配Android7.0版本更新功能

适配Android7.0版本更新功能

测试手机:华为 Android 7.0

问题1 升级安装失败,程序崩溃:

前面有一篇文章讲过了使用DownloadManager在应用内实现版本更新 详见 Android学习之—-利用DownLoadManager实现版本升级,之前Android 7.0也还没有普及所以一直没有更新代码,没注意到Android7.0的一些版本变化,导致了在Android 7.0 上面使用之前的代码更新安装程序出错,具体错误为:

     Caused by: java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated; use ContentResolver.openFileDescriptor() instead
 at android.app.DownloadManager$CursorTranslator.getString(DownloadManager.java:1503)

会抛出这样一个异常终止程序运行。那么导致整个问题的主要原因就是Android 7.0在文件管理方面更加严格,不随便对外开发文件访问权限,想要访问文件可以使用FileProvider的方式来访问,在7.0系统中,Android禁止通过file://这样的方式来访问,需要使用content://这样的方式来对文件进行访问。 所以在我之前代码中的读取文件更新就会出现这个问题,不过现在我们来对这个问题进行修复。

问题2

安装出现安装包解析异常(图片引用其他文章)

我也网上到处翻查资料 大概有这样几种情况,

1 v1,v2签名的原因,
2 apk的签名不一致
3 安装文件路径问题

也可以参考 android升级安装包–包解析错误 这篇文章,但是我只遇到过以上的2,3问题,其中2的问题好解决,保证签名一致即可,第3个问题,就又可以牵扯到7.0的安装路径问题上。只要能够找到正确的文件路径,那么就可以正确的安装。

解决办法:

既然知道了问题原因,那么我们就查找对应的解决办法。

第一步骤:了解FileProvider的使用 可以参考官网例子: https://developer.android.google.cn/reference/android/support/v4/content/FileProvider.html

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>

第二步骤:在manifest中 application中 添加provider标签,修改provider中 android:authorities为你自己的名称 建议使用包名+fileprovider
在res文件夹下面新建一个xml目录,新建一个file_paths文件,上面meta-data中使用到的。

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="download" path=""/>
</paths>

上面的代码paths中不止external-path这一种标签属性,还有其他的,这里不作详细讲解,步骤一里面给出的官网地址里面解释的比我清楚,不过声明这个地址表示 在这个对应的路径下的文件都是可以被共享访问的。

第三步骤:编写代码适配 android7.0,这里我只给出下载部分和安装部分的代码,因为对比之前 Android学习之—-利用DownLoadManager实现版本升级这篇文章除了加上第一步骤和第二步骤以外,其他的就修改了以下给出的代码,

下载部分修改实现细节

1 添加下载类型 -----> request.setMimeType("application/vnd.android.package-archive");
2 创建需要保存的File --->  downloadFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "demo.apk");
3 修改------> request.setDestinationInExternalFilesDir() 为request.setDestinationUri()
4 调用  request.setDestinationUri(Uri.fromFile(downloadFile));

更新安装部分实现细节修改

1 兼容7.0 ---判断系统版本
2 使用FileProvider获取文件uri

具体代码

下载部分

 public void gotoUpdate(Context context, String url) {
        DownloadManager downloadManager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setMimeType("application/vnd.android.package-archive"); //修改

        downloadFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "demo.apk"); //修改

        request.setDestinationUri(Uri.fromFile(downloadFile)); //修改
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setTitle("下载新版本");
        request.setVisibleInDownloadsUi(true);

        long downloadId = downloadManager.enqueue(request);
        DownCompleteReceiver downCompleteReceiver = new DownCompleteReceiver(downloadId);
        context.registerReceiver(downCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

兼容安装部分

 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
        Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", downloadFile); //修改  downloadFile 来源于上面下载文件时保存下来的
        //  BuildConfig.APPLICATION_ID + ".fileprovider" 是在manifest中 Provider里的authorities属性定义的值
        Intent installIntent = new Intent(Intent.ACTION_VIEW);
        installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //临时授权
        installIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        context.startActivity(intent);
    } else {
        // 获取下载好的 apk 路径
        String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
        // 提示用户安装
        installAPP(Uri.parse("file://" + uriString), context);
    }

总的步骤就是

1 manifest文件中添加Provider声明,声明好自己的authorities值,
2 在res/xml 目录中新增 file_paths.xml文件,声明需要共享的文件路径
3 使用DownloadManager下载文件的时候使用setDestinationUri的方式,保存事先创建的file对象,
4 通过版本判断兼容Android7.0以上以及以下的版本 
5 通过fileProvider获取到之前file对象的uri
6 添加flag 临时授权uri权限

VersionUpdate.java 可以直接拷贝一下代码测试 只需要使用VersionUpdate.newInstance().gotoUpdate()方法即可

public class VersionUpdate {

private static VersionUpdate versionUpdate = new VersionUpdate();
File downloadFile;

public static VersionUpdate newInstance() {
    return versionUpdate;
}


private VersionUpdate() {

}

public void gotoUpdate(Context context, String url) {
    DownloadManager downloadManager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
    request.setMimeType("application/vnd.android.package-archive");

    downloadFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "demo.apk");

    request.setDestinationUri(Uri.fromFile(downloadFile));
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    request.setTitle("下载新版本");
    request.setVisibleInDownloadsUi(true);

    long downloadId = downloadManager.enqueue(request);
    DownCompleteReceiver downCompleteReceiver = new DownCompleteReceiver(downloadId);
    context.registerReceiver(downCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}


public void createDialogUpdate(final Context context, final String url) {
    AlertDialog.Builder alert = new AlertDialog.Builder(context);
    alert.setTitle("更新提示")
            .setMessage("发现新版本,是否立即更新?")
            .setCancelable(false).setNeutralButton("取消", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }
    }).setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            gotoUpdate(context, url);
            dialog.dismiss();
        }
    });
    alert.create().show();
}


public class DownCompleteReceiver extends BroadcastReceiver {
    long enqueueId;

    public DownCompleteReceiver(long enqueueId) {
        this.enqueueId = enqueueId;
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        DownloadManager dm = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
        long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (enqueueId != id) {
            return;
        }

        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(enqueueId);

        Cursor c = dm.query(query);

        if (c != null && c.moveToFirst()) {
            int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
            // 下载失败也会返回这个广播,所以要判断下是否真的下载成功
            if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                    Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", downloadFile);
                    Intent installIntent = new Intent(Intent.ACTION_VIEW);
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    installIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    intent.setDataAndType(uri, "application/vnd.android.package-archive");
                    context.startActivity(intent);
                } else {
                    // 获取下载好的 apk 路径
                    String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
                    // 提示用户安装
                    installAPP(Uri.parse("file://" + uriString), context);
                }
            }
            c.close();
        }
    }

    private void installAPP(Uri data, Context context) {
        Intent promptInstall = new Intent(Intent.ACTION_VIEW)
                .setDataAndType(data, "application/vnd.android.package-archive");
        // FLAG_ACTIVITY_NEW_TASK 可以保证安装成功时可以正常打开 app
        promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(promptInstall);
    }
}

}

2017/9/19 22:16:41 好记性不如烂笔头。

猜你喜欢

转载自blog.csdn.net/xiaxiayige/article/details/78035304