Android14?、15動態(tài)申請讀寫權(quán)限實(shí)現(xiàn)方法示例代碼?(Java)
在 Android 14、15 中,Google 進(jìn)一步優(yōu)化了存儲權(quán)限系統(tǒng),特別是寫權(quán)限的管理。以下是完整的 Java 實(shí)現(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)限請求實(shí)現(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("請?jiān)趹?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)鍵點(diǎ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)限
最佳實(shí)踐:
優(yōu)先使用系統(tǒng)照片選擇器 (Photo Picker)
只在真正需要時才請求權(quán)限
提供清晰的權(quán)限解釋
正確處理所有可能的拒絕情況
兼容性考慮:
使用
Build.VERSION.SDK_INT
檢查系統(tǒng)版本為不同版本提供不同的權(quán)限請求策略
測試在各種場景下的權(quán)限行為
這套實(shí)現(xiàn)方案完全符合 Android 14 的存儲權(quán)限要求,同時保持了良好的向后兼容性。
Android 14 寫權(quán)限關(guān)鍵點(diǎ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)
最佳實(shí)踐:
優(yōu)先使用 MediaStore API 來寫入共享媒體文件
使用 SAF (Storage Access Framework) 讓用戶選擇保存位置
只在絕對必要時請求管理所有文件權(quán)限
提供清晰的權(quán)限解釋和引導(dǎo)
兼容性處理:
為不同 Android 版本提供不同的權(quán)限請求策略
使用
Environment.isExternalStorageManager()
檢查管理權(quán)限測試在各種情況下的權(quán)限行為
這套實(shí)現(xiàn)方案完全符合 Android 14 的寫權(quán)限要求,同時保持了良好的向后兼容性。
總結(jié)
到此這篇關(guān)于Android14 、15動態(tài)申請讀寫權(quán)限實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Android動態(tài)申請讀寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)短信驗(yàn)證碼登錄功能(案例)
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)短信驗(yàn)證碼登錄功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-08-08Flutter實(shí)現(xiàn)底部導(dǎo)航欄效果
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)底部導(dǎo)航欄效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08Android實(shí)現(xiàn)ListView分頁加載數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)ListView分頁加載數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11Android checkbox的listView(多選,全選,反選)具體實(shí)現(xiàn)方法
由于listview的一些特性,剛開始寫這種需求的功能的時候都會碰到一些問題,重點(diǎn)就是存儲每個checkbox的狀態(tài)值,在這里分享出了完美解決方法:2013-06-06詳解Android中Fragment的兩種創(chuàng)建方式
本篇文章主要介紹了Android中Fragment的兩種創(chuàng)建方式,具有一定的參考價值,有興趣的可以了解一下。2016-12-12Android客制化adb shell進(jìn)去后顯示shell@xxx的標(biāo)識
今天小編就為大家分享一篇關(guān)于Android客制化adb shell進(jìn)去后顯示shell@xxx的標(biāo)識,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12