我通过放弃 dart 文件保存并使用 android SAF 解决了这个问题。
首先,我所做的是尝试获取 sdCard 修改权限。
之后,我就可以保存我需要的文件了。
这是我用来获取权限的代码(也就是“允许此应用修改您的 sdCard 上的内容”对话框)
public void takeCardUriPermission(String sdCardRootPath) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
File sdCard = new File(sdCardRootPath);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
Intent intent = storageVolume.createAccessIntent(null);
try {
startActivityForResult(intent, 4010);
} catch (ActivityNotFoundException e) {
Log.e("TUNE-IN ANDROID", "takeCardUriPermission: "+e);
}
}
}
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 4010) {
Uri uri = data.getData();
grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
methodChannel.invokeMethod("resolveWithSDCardUri",getUri().toString());
}
}
public Uri getUri() {
List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
if (persistedUriPermissions.size() > 0) {
UriPermission uriPermission = persistedUriPermissions.get(0);
return uriPermission.getUri();
}
return null;
}
所以为了开始整个权限获取过程,你必须首先调用takeCardUriPermission并传递sdCard路径的URI。
注意:在我的 FlutterActivity 上,我可以使用 getExternalCacheDirs()[1].toString() 直接获取 sdCardPath
调用takeCardUriPermission 后,一旦按下允许按钮(或拒绝),将调用活动结果事件并调用 onActivtyResult 方法。 requestCode 检查在您有多个事件并且需要过滤掉这一个时很有用。
活动结果代码将授予应用修改 sdCard 上文件的权限。
getUri 方法是我们之后尝试将字节保存到文件时使用的方法,它返回我们选择的 SDCard 的 URI(您可以有多个 sdCard)。
保存文件
我用来保存文件的方法很简单。首先我们需要获取 sdCard 的 URI 并从中创建一个Documentfile,然后我们通过该目录的层次结构(DocumentFile 可以引用文件和目录)根据它的 URI 找到所需的文件。
我们通过将文件 URI 拆分为多个部分来执行此搜索,然后通过测试每个部分是否存在来导航层次结构。一旦我们测试了所有部分,我们就会到达我们的文件,如果它存在,或者我们被困在最后一个目录。
此迭代的结果是一个DocumentFile,我们可以对其执行操作。
以下是完整的文件保存代码:
String filepath = (String) arguments.get("filepath");
final byte[] bytes = methodCall.argument("bytes");
try{
if(filepath==null || bytes==null)throw new Exception("Arguments Not found");
DocumentFile documentFile = DocumentFile.fromTreeUri(getApplicationContext(), getUri());
String[] parts = filepath.split("/");
for (int i = 0; i < parts.length; i++) {
DocumentFile nextfile = documentFile.findFile(parts[i]);
if(nextfile!=null){
documentFile=nextfile;
}
}
if(documentFile!=null && documentFile.isFile()){
OutputStream out = getContentResolver().openOutputStream(documentFile.getUri());
out.write(bytes);
out.close();
}else{
throw new Exception("File Not Found");
}
}catch (Exception e){
result.error("400",e.getMessage(),e);
return;
}
result.success(true);
注意:在我的代码中,我在 MethodChannel 的 MethodCallHandler 下调用它,这将给我我需要的参数:filePath 这是我要编写的文件的字符串 URI to 和 bytes 字节数组,表示我要保存的数据。 result.success
也可以这样说
文件写入代码很简单:打开文件,写入数据,关闭文件。