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)容提供器,他使用了類(lè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)的文件,也稱(chē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ǔ)言名稱(chēng) 顯示行號(hào) 允許折疊
將數(shù)據(jù)放在這個(gè)目錄下,你可以使用之前的方法對(duì)文件進(jìn)行讀寫(xiě),不需要作出任何變更和適配。但是這個(gè)文件夾中的文件會(huì)隨著應(yīng)用卸載而被隨之刪除。 那如果需要訪問(wèn)其他目錄怎么辦呢?比如獲取相冊(cè)中的圖片,向相冊(cè)中添加一張圖片。為此,Android 系統(tǒng)針對(duì)系統(tǒng)文件類(lèi)型進(jìn)行了分類(lèi):圖片,音頻,視頻 這三類(lèi)文件可以通過(guò) MediaStore API 來(lái)進(jìn)行訪問(wèn),這種稱(chēng)為共享空間,其他的系統(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)建文件,名稱(chē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);
// 文字大?。?9dp
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 類(lèi)型
*
* @param file 文件
* @return Mime 類(lèi)型
*/
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ī)制以及handler的內(nèi)存泄露
這篇文章主要介紹了Android 消息機(jī)制以及handler的內(nèi)存泄露的相關(guān)資料,需要的朋友可以參考下2016-09-09
anroid開(kāi)發(fā)教程之spinner下拉列表的使用示例
這篇文章主要介紹了anroid的spinner下拉列表的使用示例,需要的朋友可以參考下2014-04-04
Android快速實(shí)現(xiàn)無(wú)預(yù)覽拍照功能
這篇文章主要為大家詳細(xì)介紹了Android快速實(shí)現(xiàn)無(wú)預(yù)覽拍照功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
Android性能優(yōu)化以及數(shù)據(jù)優(yōu)化方法
我和大家之前一起探討了在Android中對(duì)SQLite數(shù)據(jù)庫(kù)的操作優(yōu)化細(xì)節(jié)。今天談?wù)凙ndroid性能數(shù)據(jù)優(yōu)化方法,需要的朋友可以參考下2016-05-05
Android實(shí)現(xiàn)手機(jī)拍照功能
這篇文章主要介紹了Android實(shí)現(xiàn)手機(jī)拍照功能,感興趣的小伙伴們可以參考一下2015-12-12
Android Studio 升級(jí)到3.0 提示 java.lang.NoClassDefFoundError的解決方法
這篇文章主要介紹了Android Studio 升級(jí)到3.0 提示 java.lang.NoClassDefFoundError的解決方法,需要的朋友可以參考下2017-12-12

