随着Android版本越来越高,Android对隐私的保护力度也越来越大。这些隐私权限的更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务。如何让你的APP能够适应这些改变而不是崩溃,是每一位Android开发者必须要了解学习的。
一.引言
Android 6.0引入了动态权限控制.
Android 7.0 引入了私有目录被限制访问和StrictMode API ,在7.0上应用私有目录将被限制访问,这与iOS的沙盒机制类似,StrictMode API是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,则会报出异常。这项权限的变更将意味着你无法通过File API访问手机存储上的数据了,基于File API的一些文件浏览器等也将受到很大的影响.
所以在7.0系统上,给其他应用传递 file:// URI 类型的Uri,可能会导致接受者无法访问该路径。 因此,在Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException。解决办法就是可以通过使用FileProvider来解决这一问题.
二. 两种实现自动下载安装方式
两种下载方式入口:
/** * @param context * @param apkUrl APK下载路径 * @param fileName APK下载自定义名称 * @param webViewMode 是否是浏览器模式下载 */ public static void install(Context context, String apkUrl, String fileName, boolean webViewMode) { if (webViewMode) { downloadByWeb(context, apkUrl); } else { downloadBySelf(context, apkUrl, fileName); } }
1. 通过手机内置浏览器来下载安装更新APP.
这种方式将下载和安装更新操作交给浏览器操作了,很简单,但是这种也是不受控的.有时候浏览器会推荐一系列的广告App伴随下载,不小心就会中招.
//通过浏览器方式下载并安装 private static void downloadByWeb(Context context, String apkPath) { Uri uri = Uri.parse(apkPath); //String android.intent.action.VIEW 比较通用,会根据用户的数据类型打开相应的Activity。如:浏览器,电话,播放器,地图 Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); }
2. 通过DownloadManager 下载APK,并提示安装
2.1 通过DownloadManager
private static void downloadBySelf(Context context, String apkUrl, String fileName) { if (TextUtils.isEmpty(apkUrl)) { return; } try { Uri uri = Uri.parse(apkUrl); DownloadManager downloadManager = (DownloadManager) context .getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Request request = new DownloadManager.Request(uri); //在通知栏中显示 request.setVisibleInDownloadsUi(true); request.setTitle("应用更新"); request.setDescription("本次更新描述") //MIME_MapTable是所有文件的后缀名所对应的MIME类型的一个String数组 {".apk", "application/vnd.android.package-archive"}, request.setMimeType("application/vnd.android.package-archive"); // 在通知栏通知下载中和下载完成 // 下载完成后该Notification才会被显示 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) { // Android 3.0版本 以后才有该方法 //在下载过程中通知栏会一直显示该下载的Notification,在下载完成后该Notification会继续显示,直到用户点击该Notification或者消除该Notification request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); } String filePath = null; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//外部存储卡 filePath = Environment.getExternalStorageDirectory().getAbsolutePath(); } else { Log.i(TAG, "没有SD卡"); return; } downloadUpdateApkFilePath = filePath + File.separator + fileName + System.currentTimeMillis() + ".apk"; // 若存在,则删除 (这里具体逻辑具体看,我这里是删除) deleteFile(downloadUpdateApkFilePath); Uri fileUri = Uri.fromFile(new File(downloadUpdateApkFilePath)); request.setDestinationUri(fileUri); //下载管理Id downloadManager.enqueue(request); DownloadReceiver mDownloaderReceiver = new DownloadReceiver(); //注册下载完成广播 context.registerReceiver(mDownloaderReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } catch (Exception e) { e.printStackTrace(); //注意:如果文件下载失败则 使用浏览器下载 // downloadByWeb(context, apkUrl); } }
2.2 通过广播监控下载完成,调至提示用户安装操作.
/** * 下载完成的广播 */ public static class DownloadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!TextUtils.isEmpty(downloadUpdateApkFilePath)) { installNormal(context, downloadUpdateApkFilePath); } } }
2.3 提示用户安装或者更新
/** * 提示安装 * @param context 上下文 * @param apkPath apk下载完成在手机中的路径 */ private static void installNormal(Context context, String apkPath) { Intent intent = new Intent(Intent.ACTION_VIEW); //版本在7.0以上是不能直接通过uri访问的 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) { File file = (new File(apkPath)); // 由于没有在Activity环境下启动Activity,设置下面的标签 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //参数1:上下文, 参数2:Provider主机地址 和配置文件中保持一致,参数3:共享的文件 Uri apkUri = FileProvider.getUriForFile(context, "com.xxxxx.fileprovider", file); //添加这一句表示对目标应用临时授权该Uri所代表的文件 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive"); } context.startActivity(intent); }
三. 补充
MIME_MapTable是所有文件的后缀名所对应的MIME类型的一个String数组
//{后缀名,MIME类型} {".3gp", "video/3gpp"}, {".apk", "application/vnd.android.package-archive"}, {".asf", "video/x-ms-asf"}, {".avi", "video/x-msvideo"}, {".bin", "application/octet-stream"}, {".bmp", "image/bmp"}, {".c", "text/plain"}, {".class", "application/octet-stream"}, {".conf", "text/plain"}, {".cpp", "text/plain"}, {".doc", "application/msword"}, {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, {".xls", "application/vnd.ms-excel"}, {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, {".exe", "application/octet-stream"}, {".gif", "image/gif"}, {".gtar", "application/x-gtar"}, {".gz", "application/x-gzip"}, {".h", "text/plain"}, {".htm", "text/html"}, {".html", "text/html"}, {".jar", "application/java-archive"}, {".java", "text/plain"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"}, {".js", "application/x-javascript"}, {".log", "text/plain"}, {".m3u", "audio/x-mpegurl"}, {".m4a", "audio/mp4a-latm"}, {".m4b", "audio/mp4a-latm"}, {".m4p", "audio/mp4a-latm"}, {".m4u", "video/vnd.mpegurl"}, {".m4v", "video/x-m4v"}, {".mov", "video/quicktime"}, {".mp2", "audio/x-mpeg"}, {".mp3", "audio/x-mpeg"}, {".mp4", "video/mp4"}, {".mpc", "application/vnd.mpohun.certificate"}, {".mpe", "video/mpeg"}, {".mpeg", "video/mpeg"}, {".mpg", "video/mpeg"}, {".mpg4", "video/mp4"}, {".mpga", "audio/mpeg"}, {".msg", "application/vnd.ms-outlook"}, {".ogg", "audio/ogg"}, {".pdf", "application/pdf"}, {".png", "image/png"}, {".pps", "application/vnd.ms-powerpoint"}, {".ppt", "application/vnd.ms-powerpoint"}, {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, {".prop", "text/plain"}, {".rc", "text/plain"}, {".rmvb", "audio/x-pn-realaudio"}, {".rtf", "application/rtf"}, {".sh", "text/plain"}, {".tar", "application/x-tar"}, {".tgz", "application/x-compressed"}, {".txt", "text/plain"}, {".wav", "audio/x-wav"}, {".wma", "audio/x-ms-wma"}, {".wmv", "audio/x-ms-wmv"}, {".wps", "application/vnd.ms-works"}, {".xml", "text/plain"}, {".z", "application/x-compress"}, {".zip", "application/x-zip-compressed"}, {"", "*/*"}