隨着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"}, {"", "*/*"}