Android存儲(chǔ)權(quán)限兼容問(wèn)題解析
一、引言
在 Android 開(kāi)發(fā)中,存儲(chǔ)權(quán)限是一個(gè)常見(jiàn)且重要的權(quán)限。不同版本的 Android 系統(tǒng)對(duì)于存儲(chǔ)權(quán)限的管理和處理方式存在差異,這就導(dǎo)致了開(kāi)發(fā)者在處理存儲(chǔ)權(quán)限時(shí)會(huì)遇到各種兼容問(wèn)題。本文將詳細(xì)介紹 Android 各版本存儲(chǔ)權(quán)限的變化,以及如何進(jìn)行兼容處理,并給出具體的代碼示例。
二、Android 各版本存儲(chǔ)權(quán)限變化
2.1 Android 6.0(API 級(jí)別 23)之前
在 Android 6.0 之前,應(yīng)用的權(quán)限在安裝時(shí)一次性授予。只要用戶(hù)安裝了應(yīng)用,應(yīng)用就擁有了在 AndroidManifest.xml
中聲明的所有權(quán)限,無(wú)需在運(yùn)行時(shí)再次請(qǐng)求。例如,要使用存儲(chǔ)權(quán)限,只需在 AndroidManifest.xml
中添加如下代碼:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2.2 Android 10(API 級(jí)別 29)及之后
Android 10 引入了分區(qū)存儲(chǔ)(Scoped Storage)機(jī)制,對(duì)應(yīng)用訪(fǎng)問(wèn)外部存儲(chǔ)的方式進(jìn)行了重大改變。應(yīng)用默認(rèn)只能訪(fǎng)問(wèn)自己的專(zhuān)屬目錄(如 Context.getExternalFilesDir()
返回的目錄)和媒體文件,訪(fǎng)問(wèn)其他應(yīng)用的文件需要特殊處理。同時(shí),WRITE_EXTERNAL_STORAGE
權(quán)限不再授予普通應(yīng)用,除非應(yīng)用在 AndroidManifest.xml
中聲明 android:requestLegacyExternalStorage="true"
以使用舊的存儲(chǔ)模式。
2.3 Android 11(API 級(jí)別 30)及之后
Android 11 進(jìn)一步加強(qiáng)了分區(qū)存儲(chǔ)的限制,android:requestLegacyExternalStorage
屬性不再起作用,應(yīng)用必須適應(yīng)分區(qū)存儲(chǔ)。不過(guò),Android 11 提供了一些新的 API 來(lái)處理文件訪(fǎng)問(wèn),如 MediaStore
API 用于訪(fǎng)問(wèn)媒體文件。
三、兼容處理方案及代碼示例
3.1 權(quán)限檢查與請(qǐng)求
以下是一個(gè)在 Android 6.0 及以上版本中請(qǐng)求存儲(chǔ)權(quán)限的代碼示
import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; public class MainActivity extends AppCompatActivity { private static final int PERMISSION_REQUEST_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 檢查是否已經(jīng)授予存儲(chǔ)權(quán)限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 如果沒(méi)有授予權(quán)限,則請(qǐng)求權(quán)限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } else { // 已經(jīng)授予權(quán)限,執(zhí)行相關(guān)操作 performStorageOperation(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用戶(hù)授予了權(quán)限,執(zhí)行相關(guān)操作 performStorageOperation(); } else { // 用戶(hù)拒絕了權(quán)限,給出提示 Toast.makeText(this, "存儲(chǔ)權(quán)限被拒絕,無(wú)法執(zhí)行操作", Toast.LENGTH_SHORT).show(); } } } private void performStorageOperation() { // 執(zhí)行存儲(chǔ)相關(guān)操作 Toast.makeText(this, "開(kāi)始執(zhí)行存儲(chǔ)操作", Toast.LENGTH_SHORT).show(); } }
3.2 Android 10 及以上版本的分區(qū)存儲(chǔ)處理
在 Android 10 及以上版本中,使用分區(qū)存儲(chǔ)可以通過(guò) Context.getExternalFilesDir()
訪(fǎng)問(wèn)應(yīng)用的專(zhuān)屬目錄。以下是一個(gè)簡(jiǎn)單的文件讀寫(xiě)示例:
import android.content.Context; import android.os.Bundle; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class StorageActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_storage); // 獲取應(yīng)用的外部存儲(chǔ)專(zhuān)屬目錄 File externalFilesDir = getExternalFilesDir(null); if (externalFilesDir != null) { // 創(chuàng)建一個(gè)文件 File file = new File(externalFilesDir, "test.txt"); try { // 寫(xiě)入文件 FileOutputStream fos = new FileOutputStream(file); String content = "Hello, World!"; fos.write(content.getBytes()); fos.close(); Toast.makeText(this, "文件寫(xiě)入成功", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "文件寫(xiě)入失敗", Toast.LENGTH_SHORT).show(); } } } }
3.3 Android 11 及以上版本的媒體文件訪(fǎng)問(wèn)
import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; public class MediaAccessActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_media_access); // 獲取 ContentResolver ContentResolver contentResolver = getContentResolver(); // 定義查詢(xún)的 URI Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; // 定義查詢(xún)的列 String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME}; // 執(zhí)行查詢(xún) Cursor cursor = contentResolver.query(uri, projection, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { // 獲取圖片的 ID 和文件名 long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)); Toast.makeText(this, "圖片 ID: " + id + ", 文件名: " + displayName, Toast.LENGTH_SHORT).show(); } cursor.close(); } } }
四、總結(jié)
在 Android 開(kāi)發(fā)中,處理存儲(chǔ)權(quán)限兼容問(wèn)題需要根據(jù)不同版本的系統(tǒng)特點(diǎn)進(jìn)行相應(yīng)的處理。在 Android 6.0 及以上版本中,要使用運(yùn)行時(shí)權(quán)限機(jī)制;在 Android 10 及以上版本中,要適應(yīng)分區(qū)存儲(chǔ);在 Android 11 及以上版本中,要使用新的 API 進(jìn)行文件訪(fǎng)問(wèn)。通過(guò)合理的權(quán)限管理和文件操作,可以確保應(yīng)用在不同版本的 Android 系統(tǒng)上都能正常運(yùn)行。
以上就是Android存儲(chǔ)權(quán)限兼容問(wèn)題解析的詳細(xì)內(nèi)容,更多關(guān)于Android存儲(chǔ)權(quán)限兼容的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android網(wǎng)絡(luò)請(qǐng)求-sign參數(shù)的設(shè)置方式
這篇文章主要介紹了Android網(wǎng)絡(luò)請(qǐng)求-sign參數(shù)的設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android自定義ViewGroup之WaterfallLayout(二)
這篇文章主要為大家詳細(xì)介紹了Android自定義ViewGroup之WaterfallLayout,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android檢測(cè)手機(jī)多點(diǎn)觸摸點(diǎn)數(shù)的方法
這篇文章主要為大家詳細(xì)介紹了Android檢測(cè)手機(jī)多點(diǎn)觸摸點(diǎn)數(shù)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05去除arraylist容器中的相同的對(duì)象元素的方法
下面小編就為大家?guī)?lái)一篇去除arraylist容器中的相同的對(duì)象元素的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-096步輕松實(shí)現(xiàn)兩個(gè)listView聯(lián)動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了教大家通過(guò)6步輕松實(shí)現(xiàn)兩個(gè)listView聯(lián)動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Flutter?DateTime獲取本月的開(kāi)始時(shí)間與結(jié)束時(shí)間方法
這篇文章主要為大家介紹了Flutter?DateTime獲取本月的開(kāi)始時(shí)間與結(jié)束時(shí)間方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2023-05-05Android自定義布局實(shí)現(xiàn)仿qq側(cè)滑部分代碼
這篇文章主要為大家詳細(xì)介紹了自定義布局實(shí)現(xiàn)仿qq側(cè)滑Android部分代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例
這篇文章介紹了Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-10-10Android百度地圖定位后獲取周邊位置的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android百度地圖定位后獲取周邊位置的實(shí)現(xiàn)代碼,準(zhǔn)確獲取周邊地理位置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01Android Java調(diào)用自己C++類(lèi)庫(kù)的實(shí)例講解
今天小編就為大家分享一篇關(guān)于Android Java調(diào)用自己C++類(lèi)庫(kù)的實(shí)例講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02