一、AndroidManifest.xml 权限配置
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
...
android:requestLegacyExternalStorage="true">
...
</application>
二、权限适配类
public class StoragePermissionHelper {
// 权限请求码常量
private static final int REQUEST_CODE_READ_MEDIA = 0x00000003;
private static final int REQUEST_CODE_STORAGE_10_12 = 0x00000005;
private static final int REQUEST_CODE_STORAGE_6_9 = 0x00000006;
private final Activity activity;
private StorageCallback callback;
public StoragePermissionHelper(Activity activity, StorageCallback callback) {
this.activity = activity;
this.callback = callback;
}
public interface StorageCallback {
void onStorageReady(OutputStream outputStream, String errorMessage);
void onPermissionRequired(String[] permissions, int requestCode);
}
// 主入口方法
public void requestFileWriteAccess(String fileName, String mimeType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
handleModernAndroid(fileName, mimeType);
} else {
handleLegacyAndroid(fileName);
}
}
private void handleModernAndroid(String fileName, String mimeType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
handleAndroid13Plus(fileName, mimeType);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
handleAndroid11_12(fileName, mimeType);
} else {
handleAndroid10(fileName, mimeType);
}
} else {
handleAndroid6_9(fileName);
}
}
private void handleAndroid13Plus(String fileName, String mimeType) {
if (checkSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) {
writeViaMediaStore(fileName, mimeType);
} else {
requestPermission(new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_CODE_READ_MEDIA);
}
}
private void handleAndroid11_12(String fileName, String mimeType) {
if (Environment.isExternalStorageManager()) {
writeViaMediaStore(fileName, mimeType);
} else {
requestAllFilesAccessPermission();
}
}
private void handleAndroid10(String fileName, String mimeType) {
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
writeViaMediaStore(fileName, mimeType);
} else {
requestPermission(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, REQUEST_CODE_STORAGE_10_12);
}
}
private void handleAndroid6_9(String fileName) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
writeViaDirectPath(fileName);
} else {
requestPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE_6_9);
}
}
private void handleLegacyAndroid(String fileName) {
writeViaDirectPath(fileName);
}
private void writeViaMediaStore(String fileName, String mimeType) {
OutputStream outputStream = null;
String errorMessage = "";
try {
Uri uri = MediaStore.Files.getContentUri("external");
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
ContentResolver contentResolver = activity.getContentResolver();
Uri uriForInsertResult = contentResolver.insert(uri, contentValues);
if (uriForInsertResult != null) {
outputStream = contentResolver.openOutputStream(uriForInsertResult);
} else {
errorMessage = "Failed to create file in MediaStore";
}
} catch (IOException e) {
e.printStackTrace();
errorMessage = e.getMessage();
}
callback.onStorageReady(outputStream, errorMessage);
}
private void writeViaDirectPath(String fileName) {
OutputStream outputStream = null;
String errorMessage = "";
try {
File externalStorageDirectory = Environment.getExternalStorageDirectory();
File file = new File(externalStorageDirectory, fileName);
outputStream = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
errorMessage = e.getMessage();
}
callback.onStorageReady(outputStream, errorMessage);
}
private void requestPermission(String[] permissions, int requestCode) {
callback.onPermissionRequired(permissions, requestCode);
}
private void requestAllFilesAccessPermission() {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
activity.startActivityForResult(intent, REQUEST_CODE_STORAGE_10_12);
} catch (Exception e) {
e.printStackTrace();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
activity.startActivityForResult(intent, REQUEST_CODE_STORAGE_10_12);
}
}
private int checkSelfPermission(String permission) {
return ContextCompat.checkSelfPermission(activity, permission);
}
// 处理权限请求结果
public void handlePermissionResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 重新尝试文件操作
String fileName = "buyup_导出资料_" + System.currentTimeMillis() + ".xls";
switch (requestCode) {
case REQUEST_CODE_READ_MEDIA:
writeViaMediaStore(fileName, "application/x-xls");
break;
case REQUEST_CODE_STORAGE_10_12:
writeViaMediaStore(fileName, "application/x-xls");
break;
case REQUEST_CODE_STORAGE_6_9:
writeViaDirectPath(fileName);
break;
}
} else {
Toast.makeText(activity, "权限授予失败", Toast.LENGTH_LONG).show();
}
}
// 处理特殊权限请求结果(Android 11-12)
public void handleActivityResult(int requestCode) {
if (requestCode == REQUEST_CODE_STORAGE_10_12) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
Environment.isExternalStorageManager()) {
String fileName = "buyup_导出资料_" + System.currentTimeMillis() + ".xls";
writeViaMediaStore(fileName, "application/x-xls");
} else {
Toast.makeText(activity, "需要授予权限才能导出文件!", Toast.LENGTH_SHORT).show();
}
}
}
}
三、使用示例
public class MainActivity extends AppCompatActivity {
private StoragePermissionHelper storageHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
storageHelper = new StoragePermissionHelper(this, new StoragePermissionHelper.StorageCallback() {
@Override
public void onStorageReady(OutputStream outputStream, String errorMessage) {
if (outputStream != null) {
// 执行文件写入操作
writeDataToOutputStream(outputStream);
} else {
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_LONG).show();
}
}
@Override
public void onPermissionRequired(String[] permissions, int requestCode) {
requestPermissions(permissions, requestCode);
}
});
// 触发文件写入
Button exportButton = findViewById(R.id.export_button);
exportButton.setOnClickListener(v -> {
String fileName = "test_导出资料_" + System.currentTimeMillis() + ".xls";
storageHelper.requestFileWriteAccess(fileName, "application/x-xls");
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
storageHelper.handlePermissionResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
storageHelper.handleActivityResult(requestCode);
}
private void writeDataToOutputStream(OutputStream outputStream) {
// 实现您的文件写入逻辑
try {
// 示例:写入一些数据
outputStream.write("测试数据".getBytes());
outputStream.close();
Toast.makeText(this, "文件导出成功", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "文件写入失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}