Android各个版本存储权限适配与文件写入

一、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();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值