You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Android中SAF保存Tree URI的正确方法(目标SDK Marshmallow)

嘿,这个问题我之前也碰到过!在Xamarin.Android里用SAF持久化外部SD卡的访问权限,直接存Uri的字符串确实会在重启后失效,核心问题是你没正确获取并保存持久化的权限令牌。下面是具体的解决步骤:

1. 获取Tree URI时立即申请持久化权限

当用户通过ACTION_OPEN_DOCUMENT_TREE选择完目录后,你不能直接把Uri转成字符串就存起来,必须先调用ContentResolver.TakePersistableUriPermission来获取长期访问权限。这个方法会把权限和你的应用签名绑定,重启后依然有效(除非用户手动撤销权限)。

示例代码:

// 定义请求码常量
private const int ACTION_OPEN_DOCUMENT_TREE = 1001;

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    
    if (requestCode == ACTION_OPEN_DOCUMENT_TREE && resultCode == Result.Ok)
    {
        var treeUri = data.Data;
        if (treeUri != null)
        {
            // 申请读写的持久化权限
            var permissionFlags = ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission;
            ContentResolver.TakePersistableUriPermission(treeUri, permissionFlags);
            
            // 把Uri字符串保存到SharedPreferences
            var prefs = GetSharedPreferences("AppStoragePrefs", FileCreationMode.Private);
            using (var editor = prefs.Edit())
            {
                editor.PutString("SavedTreeUri", treeUri.ToString());
                editor.Apply();
            }
        }
    }
}

2. 重启后恢复Uri并验证权限

从SharedPreferences取出Uri字符串解析成Android.Net.Uri后,一定要先检查系统是否还保留着这个Uri的持久化权限。如果权限失效,就得重新引导用户选择目录。

示例代码:

private Android.Net.Uri GetPersistedTreeUri()
{
    var prefs = GetSharedPreferences("AppStoragePrefs", FileCreationMode.Private);
    var savedUriStr = prefs.GetString("SavedTreeUri", null);
    
    if (string.IsNullOrEmpty(savedUriStr))
        return null;
    
    var treeUri = Android.Net.Uri.Parse(savedUriStr);
    
    // 检查是否还有有效的持久化权限
    bool hasValidPermission = false;
    var persistedPermissions = ContentResolver.PersistedUriPermissions;
    
    foreach (var permission in persistedPermissions)
    {
        if (permission.Uri.Equals(treeUri) && 
            (permission.Flags & (ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission)) != 0)
        {
            hasValidPermission = true;
            break;
        }
    }
    
    if (!hasValidPermission)
    {
        // 权限已失效,重新请求用户选择目录
        var intent = new Intent(Intent.ActionOpenDocumentTree);
        StartActivityForResult(intent, ACTION_OPEN_DOCUMENT_TREE);
        return null;
    }
    
    return treeUri;
}

关键注意事项

  • 必须在首次获取Uri时调用TakePersistableUriPermission:如果跳过这一步,即使保存了Uri字符串,重启后系统也不会认可你的访问权限。
  • 权限和应用签名绑定:只要你的应用签名不变,持久化权限会一直保留,除非用户手动在应用设置里撤销权限,或者卸载重装应用。
  • 不要尝试解析Uri为本地文件路径:SAF的Uri是内容提供者的标识,不是直接的文件系统路径,所有读写操作都要通过ContentResolver来完成。

内容的提问来源于stack exchange,提问作者Amit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:32:18