Android14?、15動態(tài)申請讀寫權(quán)限實現(xiàn)方法示例代碼?(Java)
在 Android 14、15 中,Google 進一步優(yōu)化了存儲權(quán)限系統(tǒng),特別是寫權(quán)限的管理。以下是完整的 Java 實現(xiàn)方案:
1. AndroidManifest.xml 聲明權(quán)限
<strong><!-- Android 14 存儲權(quán)限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 新增的視覺媒體選擇權(quán)限 -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /></strong>
<!-- 兼容舊版本 (可選) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" /><!-- 寫入權(quán)限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" /> <!-- 僅用于兼容舊版本 -->2. Java 權(quán)限請求實現(xiàn)
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class StoragePermissionHelper extends AppCompatActivity {
// 多權(quán)限請求啟動器
private final ActivityResultLauncher<String[]> requestPermissionsLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
this::handlePermissionResult);
// 檢查并請求存儲權(quán)限
public void checkAndRequestStoragePermissions() {
List<String> permissionsToRequest = new ArrayList<>();
// Android 14+ 需要的權(quán)限(讀、寫權(quán)限) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_MEDIA_IMAGES); }
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_MEDIA_VIDEO); }
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED); }
}
// Android 13(讀、寫權(quán)限) else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_MEDIA_IMAGES);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_MEDIA_VIDEO);
}
}
// Android 10-12(讀、寫權(quán)限) else if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
}
if (permissionsToRequest.isEmpty()) {
onStoragePermissionGranted();
} else {
requestPermissionsLauncher.launch(permissionsToRequest.toArray(new String[0]));
}
}
// 處理權(quán)限請求結(jié)果
private void handlePermissionResult(Map<String, Boolean> permissions) {
List<String> deniedPermissions = new ArrayList<>();
for (Map.Entry<String, Boolean> entry : permissions.entrySet()) {
if (!entry.getValue()) {
deniedPermissions.add(entry.getKey());
}
}
if (deniedPermissions.isEmpty()) {
onStoragePermissionGranted();
} else {
handleDeniedPermissions(deniedPermissions);
}
}
// 權(quán)限全部授予
private void onStoragePermissionGranted() {
Toast.makeText(this, "存儲權(quán)限已授予", Toast.LENGTH_SHORT).show();
// 這里可以執(zhí)行需要權(quán)限的操作
}
// 處理被拒絕的權(quán)限
private void handleDeniedPermissions(List<String> deniedPermissions) {
for (String permission : deniedPermissions) {
if (shouldShowRequestPermissionRationale(permission)) {
showRationaleDialog(permission);
} else {
showGoToSettingsDialog(permission);
}
}
}
// 顯示權(quán)限解釋對話框
private void showRationaleDialog(String permission) {
new AlertDialog.Builder(this)
.setTitle("需要權(quán)限")
.setMessage(getPermissionMessage(permission))
.setPositiveButton("確定", (dialog, which) ->
checkAndRequestStoragePermissions())
.setNegativeButton("取消", null)
.show();
}
// 顯示前往設(shè)置對話框
private void showGoToSettingsDialog(String permission) {
new AlertDialog.Builder(this)
.setTitle("權(quán)限被永久拒絕")
.setMessage("請在應(yīng)用設(shè)置中手動授予" + getPermissionName(permission) + "權(quán)限")
.setPositiveButton("去設(shè)置", (dialog, which) -> {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
})
.setNegativeButton("取消", null)
.show();
}
// 獲取權(quán)限說明信息
private String getPermissionMessage(String permission) {
switch (permission) {
case Manifest.permission.READ_MEDIA_IMAGES:
return "需要訪問您的照片以提供完整功能";
case Manifest.permission.READ_MEDIA_VIDEO:
return "需要訪問您的視頻以提供完整功能";
case Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED:
return "需要訪問您選擇的媒體文件";
case Manifest.permission.READ_EXTERNAL_STORAGE:
return "需要訪問您的文件以提供完整功能";
default:
return "需要此權(quán)限以提供完整功能";
}
}
// 獲取權(quán)限名稱
private String getPermissionName(String permission) {
switch (permission) {
case Manifest.permission.READ_MEDIA_IMAGES:
return "照片訪問";
case Manifest.permission.READ_MEDIA_VIDEO:
return "視頻訪問";
case Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED:
return "選擇的媒體文件訪問";
case Manifest.permission.READ_EXTERNAL_STORAGE:
return "文件訪問";
default:
return "存儲";
}
}
}3. 使用示例
public class MainActivity extends StoragePermissionHelper {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_request_storage).setOnClickListener(v -> {
checkAndRequestStoragePermissions();
});
}
@Override
protected void onResume() {
super.onResume();
// 從設(shè)置返回后檢查權(quán)限狀態(tài)
verifyStoragePermissions();
}
private void verifyStoragePermissions() {
boolean hasPermissions;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
hasPermissions = ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_MEDIA_VIDEO) == PackageManager.PERMISSION_GRANTED;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
hasPermissions = ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED;
} else {
hasPermissions = ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
if (hasPermissions) {
onStoragePermissionGranted();
}
}
}Android 14 存儲權(quán)限關(guān)鍵點
新增權(quán)限:
READ_MEDIA_VISUAL_USER_SELECTED: 允許用戶選擇特定媒體文件其他媒體權(quán)限與 Android 13 相同
權(quán)限策略變化:
完全移除了
READ_EXTERNAL_STORAGE對媒體文件的控制應(yīng)用默認(rèn)只能訪問自己創(chuàng)建的文件
訪問其他媒體文件必須請求權(quán)限
最佳實踐:
優(yōu)先使用系統(tǒng)照片選擇器 (Photo Picker)
只在真正需要時才請求權(quán)限
提供清晰的權(quán)限解釋
正確處理所有可能的拒絕情況
兼容性考慮:
使用
Build.VERSION.SDK_INT檢查系統(tǒng)版本為不同版本提供不同的權(quán)限請求策略
測試在各種場景下的權(quán)限行為
這套實現(xiàn)方案完全符合 Android 14 的存儲權(quán)限要求,同時保持了良好的向后兼容性。
Android 14 寫權(quán)限關(guān)鍵點
主要變化:
Android 14 繼續(xù)限制
WRITE_EXTERNAL_STORAGE的使用需要
MANAGE_EXTERNAL_STORAGE權(quán)限才能管理所有文件必須引導(dǎo)用戶到系統(tǒng)設(shè)置手動開啟"管理所有文件"權(quán)限
權(quán)限策略:
應(yīng)用默認(rèn)只能寫入自己的專屬目錄
寫入共享存儲需要相應(yīng)權(quán)限
管理所有文件需要用戶顯式授權(quán)
最佳實踐:
優(yōu)先使用 MediaStore API 來寫入共享媒體文件
使用 SAF (Storage Access Framework) 讓用戶選擇保存位置
只在絕對必要時請求管理所有文件權(quán)限
提供清晰的權(quán)限解釋和引導(dǎo)
兼容性處理:
為不同 Android 版本提供不同的權(quán)限請求策略
使用
Environment.isExternalStorageManager()檢查管理權(quán)限測試在各種情況下的權(quán)限行為
這套實現(xiàn)方案完全符合 Android 14 的寫權(quán)限要求,同時保持了良好的向后兼容性。
總結(jié)
到此這篇關(guān)于Android14 、15動態(tài)申請讀寫權(quán)限實現(xiàn)方法的文章就介紹到這了,更多相關(guān)Android動態(tài)申請讀寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實現(xiàn)短信驗證碼登錄功能(案例)
這篇文章主要介紹了SpringBoot實現(xiàn)短信驗證碼登錄功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-08-08
Android實現(xiàn)ListView分頁加載數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)ListView分頁加載數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
Android checkbox的listView(多選,全選,反選)具體實現(xiàn)方法
由于listview的一些特性,剛開始寫這種需求的功能的時候都會碰到一些問題,重點就是存儲每個checkbox的狀態(tài)值,在這里分享出了完美解決方法:2013-06-06
詳解Android中Fragment的兩種創(chuàng)建方式
本篇文章主要介紹了Android中Fragment的兩種創(chuàng)建方式,具有一定的參考價值,有興趣的可以了解一下。2016-12-12
Android客制化adb shell進去后顯示shell@xxx的標(biāo)識
今天小編就為大家分享一篇關(guān)于Android客制化adb shell進去后顯示shell@xxx的標(biāo)識,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12

