Android 版本、權(quán)限適配相關(guān)總結(jié)
請(qǐng)求存儲(chǔ)權(quán)限
首先需要在 AndroidManifest.xml 文件中聲明權(quán)限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
在代碼中請(qǐng)求用戶權(quán)限:
// 權(quán)限請(qǐng)求碼 private static final int PERMISSION_REQ_ID = 0; // 請(qǐng)求權(quán)限 private static final String[] REQUESTED_PERMISSIONS = { Manifest.permission.READ_EXTERNAL_STORAGE }; ... // 判斷有沒(méi)有存儲(chǔ)權(quán)限 if (checkSelfPermission(REQUESTED_PERMISSIONS[0],PERMISSION_REQ_ID)){ //YSE }else { //NO } private boolean checkSelfPermission(String permissions,int requestCode){ if (ContextCompat.checkSelfPermission(this,permissions) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode); return false; } return true; } // 重寫(xiě)此方法,接收用戶授權(quán)回調(diào) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { Log.i(TAG, "onRequestPermissionsResult: requestCode =" + requestCode +"\n,permissions =" + Arrays.toString(permissions) +"\n,grantResults =" + Arrays.toString(grantResults)); if (requestCode == PERMISSION_REQ_ID){ if (grantResults[0] == PackageManager.PERMISSION_GRANTED){ //用戶同意權(quán)限 }else { //用戶拒絕權(quán)限 } }
版本適配
從 Android 6.0 到 Android 10 存儲(chǔ)/訪問(wèn)文件功能,有發(fā)生了很多變化。
Android 7.0 前
在Android 7.0 以前我們?cè)L問(wèn)內(nèi)存中的文件可以通過(guò) Uri.fromFile,將 File 轉(zhuǎn)換成 Uri 對(duì)象,這個(gè) Uri 對(duì)象表示這本地真實(shí)路徑。 訪問(wèn)一個(gè)圖片:
String fileName = "default_Image.jpg"; File file = new File("file_path", fileName); Uri uri = Uri.fromFile(file);
Android 7.0 后
在 7.0 后,這種通過(guò)真實(shí)路徑來(lái)獲取的 Uri 被認(rèn)為是不安全的,所以提供了一種新的解決方案,就是通過(guò) FileProvide 來(lái)實(shí)現(xiàn)文件的訪問(wèn),F(xiàn)ileProvider 是一種比較特殊的內(nèi)容提供器,他使用了類似于內(nèi)容提供器的機(jī)制來(lái)對(duì)數(shù)據(jù)進(jìn)行保護(hù)。 訪問(wèn)一個(gè)圖片:
File file = new File(CACHE_IMG, "file_name"); Uri imageUri = FileProvider.getUriForFile(activity,"com.sandan.fileprovider", file); //這里進(jìn)行替換uri的獲得方式
然而上面這種真的好嗎,對(duì)用開(kāi)發(fā)者而且這算是好處吧,但是對(duì)用用戶而言,上述的無(wú)疑一些流氓作用,因?yàn)殚_(kāi)發(fā)者完全可以訪問(wèn)的內(nèi)存中的所有位置,并作出一些改變,導(dǎo)致 SD 卡中的空間變得非常亂,即使卸載了 app,但是一些垃圾文件卻還在內(nèi)存中。
Android 10.0
在 Android 10.0 ,為了解決上述問(wèn)題,Google 在 Android 10.0 中加入了 作用域功能。
什么是作用域
就是 Android 系統(tǒng)對(duì) SD 卡做了很大的限制,從 Android 10.0 開(kāi)始,每個(gè)程序只能有權(quán)在自己的外置存儲(chǔ)空間關(guān)聯(lián)的目錄下讀取和創(chuàng)建相應(yīng)的文件,也稱作沙箱。獲取該目錄的代碼是:getExternalFilesDir() ,關(guān)聯(lián)的目錄路徑大致如下:
Html CSS JavaScript Vb vbs Asp PHP Perl Python Ruby C# C++ SQL Delphi Diff Groovy Java JavaFX ActionScript3 Bash/shell powershell Plain Text Scala XML顯示語(yǔ)言名稱 顯示行號(hào) 允許折疊
將數(shù)據(jù)放在這個(gè)目錄下,你可以使用之前的方法對(duì)文件進(jìn)行讀寫(xiě),不需要作出任何變更和適配。但是這個(gè)文件夾中的文件會(huì)隨著應(yīng)用卸載而被隨之刪除。 那如果需要訪問(wèn)其他目錄怎么辦呢?比如獲取相冊(cè)中的圖片,向相冊(cè)中添加一張圖片。為此,Android 系統(tǒng)針對(duì)系統(tǒng)文件類型進(jìn)行了分類:圖片,音頻,視頻 這三類文件可以通過(guò) MediaStore API 來(lái)進(jìn)行訪問(wèn),這種稱為共享空間,其他的系統(tǒng)文件需要使用 系統(tǒng)的文件選擇器來(lái)進(jìn)行訪問(wèn),另外,如果程序向媒體庫(kù)寫(xiě)入圖片,視頻,音頻,將會(huì)自動(dòng)用于讀寫(xiě)權(quán)限,不需要額外申請(qǐng)權(quán)限,如果你要讀取其他程序向媒體貢獻(xiàn)的圖片,視頻,音頻,則必須要申請(qǐng) READ_EXTERNAL_STORAGE 權(quán)限,WRITE_EXTERNAL_STORAGE 權(quán)限會(huì)在未來(lái)的版本中被廢棄。
舉個(gè)栗子
舉例說(shuō)明:有一張本地圖片,向這張圖片添加水印,并保存到相冊(cè)。
直接上代碼:
/** * 保存圖片到相冊(cè) * * @param context 上下文 * @param text 水印文字 */ private void savePhotoAlbum(final Context context, final String text) { //這里開(kāi)啟子線程,防止堵塞。 new Thread(new Runnable() { @Override public void run() { try { //從本地獲取一張圖片,轉(zhuǎn)成Bitmap Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_info); //在沙箱中創(chuàng)建文件,名稱:info.jpg File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "info.jpg"); //判斷文件是否存在,不存在創(chuàng)建文件。 if (!file.exists()) { file.createNewFile(); } // 向圖片添加水印 Bitmap newBitmap = addInfoWatermark(context, bitmap, text); // 更新相冊(cè) updatePhotoAlbum(context, newBitmap, file); } catch (Exception e) { e.printStackTrace(); } } }).start(); //開(kāi)始線程 } /** * 保存到相冊(cè) * * @param context 上下文 * @param src 源圖片 * @param text 水印文字 */ private Bitmap addInfoWatermark(final Context context, Bitmap src, String text) { //判斷圖片/水印文字 是否為空 if (isEmptyBitmap(src) || text == null ) { return null; } // 從源圖片復(fù)制一份 Bitmap ret = src.copy(src.getConfig(), true); // 初始化畫(huà)筆 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // 初始化畫(huà)布 Canvas canvas = new Canvas(ret); // 水印文字:黑色 paint.setColor(Color.BLACK); // 文字大小:19dp paint.setTextSize(dip2px(context, 19)); // 開(kāi)始繪畫(huà) canvas.drawText(text, 10, 10 , paint); // 循環(huán)利用資源 if (!src.isRecycled()) { src.recycle(); } return ret; } /** * 保存到相冊(cè) * * @param context 上下文 * @param src 源圖片 * @param file 要保存到的文件 */ private void savePhotoAlbum(final Context context, Bitmap src, final File file) { //判斷圖片 是否為空 if (isEmptyBitmap(src)) { return; } // 保存文件 OutputStream outputStream; try { //輸出這個(gè)文件 outputStream = new BufferedOutputStream(new FileOutputStream(file)); // 壓縮 src.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); // 循環(huán)利用資源 if (!src.isRecycled()) { src.recycle(); } } catch (FileNotFoundException e) { e.printStackTrace(); } // 更新圖庫(kù),這個(gè)在 Android 6.0 和 Android 10.0 更新圖庫(kù),存在差異。 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10.0 及以上 // 創(chuàng)建 ContentValues 對(duì)象,準(zhǔn)備插入數(shù)據(jù) ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName()); values.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(file)); values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM); ContentResolver contentResolver = context.getContentResolver(); // 插入數(shù)據(jù),返回所插入數(shù)據(jù)對(duì)應(yīng)的Uri Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (uri == null) { return; } try { // 獲取剛插入的數(shù)據(jù)的Uri對(duì)應(yīng)的輸出流 outputStream = contentResolver.openOutputStream(uri); FileInputStream fileInputStream = new FileInputStream(file); // 從一個(gè)流復(fù)制到另一個(gè)流上 FileUtils.copy(fileInputStream, outputStream); //關(guān)閉流 fileInputStream.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } else { // android 6.0 - 10.0 // 掃描文件 MediaScannerConnection.scanFile( context.getApplicationContext(), new String[]{file.getAbsolutePath()}, new String[]{"image/jpeg"}, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { //通知相冊(cè)更新 // 插入圖片 MediaStore.Images.Media.insertImage( context.getContentResolver(), BitmapFactory.decodeFile(path), file.getName(), null); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri u = Uri.fromFile(file); intent.setData(u); context.sendBroadcast(intent); // 發(fā)廣播通知,更新相冊(cè) } }); } } /** * Bitmap對(duì)象是否為空。 */ private static boolean isEmptyBitmap(Bitmap src) { return src == null || src.getWidth() == 0 || src.getHeight() == 0; } /** * 獲取 Mime 類型 * * @param file 文件 * @return Mime 類型 */ private static String getMimeType(File file) { FileNameMap fileNameMap = URLConnection.getFileNameMap(); String type = fileNameMap.getContentTypeFor(file.getName()); return type; } /** * 根據(jù)手機(jī)的分辨率從 px(像素) 的單位 轉(zhuǎn)成為 dp */ public int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }
以上就是Android 版本、權(quán)限適配相關(guān)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Android 版本、權(quán)限適配的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android廣角相機(jī)畸變校正算法和實(shí)現(xiàn)示例
今天小編就為大家分享一篇android廣角相機(jī)畸變校正算法和實(shí)現(xiàn)示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08分析Android 11.0Settings源碼之主界面加載
這篇文章主要介紹了分析Android 11.0Settings源碼之主界面加載,對(duì)Android源碼感興趣的同學(xué),可以著重看一下2021-04-04Android設(shè)置Padding和Margin(動(dòng)態(tài)/靜態(tài))的方法實(shí)例
如何在java代碼中設(shè)置margin,也就是組件與組件之間的間距,下面這篇文章主要給大家介紹了關(guān)于Android設(shè)置Padding和Margin(動(dòng)態(tài)/靜態(tài))的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11Android WebView如何判斷是否滾動(dòng)到底部
大家好,本篇文章主要講的是Android WebView如何判斷是否滾動(dòng)到底部,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01使用DrawerLayout完成滑動(dòng)菜單的示例代碼
這篇文章主要介紹了使用DrawerLayout完成滑動(dòng)菜單的示例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Android啟動(dòng)初始化方案App StartUp的應(yīng)用詳解
這篇文章主要介紹了Android啟動(dòng)初始化方案App StartUp的使用方法,StartUp是為了App的啟動(dòng)提供的一套簡(jiǎn)單、高效的初始化方案,下面我們來(lái)詳細(xì)了解2022-09-09android webvie指定視頻播放器播放網(wǎng)站視頻
android webview過(guò)濾調(diào)用第三方瀏覽器,并且解析視頻網(wǎng)站播放地址,使用指定播放器2013-11-11