【发布时间】:2023-03-03 01:11:02
【问题描述】:
注意:我已经回答了关于类似例外的其他问题。这不是重复的 - 它们都不是关于选择 PDF 或这个特定错误的。
系好安全带。
我们正在开发一个 Xamarin.Android 项目。有一个'附加文档' 活动。
当用户尝试(从下载目录)选择 PDF 时,会引发以下异常:
Java.Lang.SecurityException:权限拒绝:读取 com.android.providers.downloads.DownloadStorageProvider uri content://com.android.providers.downloads.documents/226 来自 pid=13877, uid=10282 要求您使用 ACTION_OPEN_DOCUMENT 或相关 API
文件选择器意图:
ChooseFile.Click += (sender, e) =>
{
Intent intent = new Intent();
intent.SetType("*/*");
intent.SetAction(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.N)
{
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
intent.AddFlags(ActivityFlags.GrantPersistableUriPermission);
intent.PutExtra(Intent.ExtraLocalOnly, true);
intent.AddFlags(ActivityFlags.NoHistory);
}
try
{
StartActivityForResult(Intent.CreateChooser(intent, "Select File"), FILE_SELECT_CODE);
}
catch (Exception ex)
{
Toast.MakeText(this, "Please install a File Manager.", ToastLength.Short).Show();
}
};
OnActivityResult 方法:
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if ((requestCode == FILE_SELECT_CODE) && (resultCode == Result.Ok) && (data != null))
{
// Get the Uri of the selected file
uri = data.Data;
string path = "";
bool isdoc = DocumentsContract.IsDocumentUri(this, uri);
if (isdoc)
{
if (IsDownloadsDocument(uri))
{
string id = DocumentsContract.GetDocumentId(uri);
global::Android.Net.Uri contentUri = null;
if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.N)
{
contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://com.android.providers.downloads.documents"), Convert.ToInt64(id));
}
else
{
contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://downloads/public_downloads"), Convert.ToInt64(id));
}
path = GetDataColumn(this, contentUri, null, null);
}
else if (IsMediaDocument(uri))
{
string docId = DocumentsContract.GetDocumentId(uri);
string[] split = docId.Split(':');
string type = split[0];
global::Android.Net.Uri contentUri = null;
if ("image".Equals(type))
{
contentUri = MediaStore.Images.Media.ExternalContentUri;
}
else if ("video".Equals(type))
{
contentUri = MediaStore.Video.Media.ExternalContentUri;
}
else if ("audio".Equals(type))
{
contentUri = MediaStore.Audio.Media.ExternalContentUri;
}
string selection = "_id=?";
string[] selectionArgs = new String[]
{
split[1]
};
path = GetDataColumn(this, contentUri, selection, selectionArgs);
}
}
else
{
path = GetRealPathFromURI(global::Android.Net.Uri.Parse(getImageUrlWithAuthority(this, uri)));
}
txtFileName.Text = System.IO.Path.GetFileName(path);
fileStream = File.ReadAllBytes(path);
}
}
检查文件目录 URI:
private bool IsExternalStorageDocument(global::Android.Net.Uri uri)
{
return "com.android.externalstorage.documents".Equals(uri.Authority);
}
private bool IsDownloadsDocument(global::Android.Net.Uri uri)
{
return "com.android.providers.downloads.documents".Equals(uri.Authority);
}
private bool IsMediaDocument(global::Android.Net.Uri uri)
{
return "com.android.providers.media.documents".Equals(uri.Authority);
}
private bool IsGooglePhotosUri(global::Android.Net.Uri uri)
{
return "com.google.android.apps.photos.content".Equals(uri.Authority);
}
GetDataColumn 方法:
private string GetDataColumn(Context context, global::Android.Net.Uri uri, String selection, string[] selectionArgs)
{
ICursor cursor = null;
string column = "_data";
string[] projection =
{
column
};
try
{
cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.MoveToFirst())
{
int index = cursor.GetColumnIndexOrThrow(column);
return cursor.GetString(index);
}
}
finally
{
if (cursor != null)
cursor.Close();
}
return null;
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jnbmedical.portal"
android:installLocation="auto"
android:versionCode="33"
android:versionName="3.3">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<application android:label="JandB" android:icon="@drawable/icon">
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.jnbmedical.portal.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.jandbmedical.portal.testapp.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>
file_provider_paths.xml
<?xml version="1.0" encoding="utf-8" ?>
<paths>
<external-path
name="external_files" path="." />
</paths>
我们最近添加了运行时权限(在启动屏幕活动中)以处理 Android 23(即 6.0 - marshmallow)及更高版本。
权限字段和数组:
const int REQUEST = 0;
readonly string[] PERMISSIONS =
{
Manifest.Permission.AccessCoarseLocation,
Manifest.Permission.AccessFineLocation,
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.WriteExternalStorage,
Manifest.Permission.ReadPhoneState,
};
CheckPermissions 方法:
private void CheckPermissions()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneState) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != Permission.Granted
|| ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
//Permissions have not been granted
ActivityCompat.RequestPermissions(this, PERMISSIONS, REQUEST);
}
else
{
//Permissions have been granted
TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
Util.UUID = tm.DeviceId;
StartActivity(typeof(MainActivity));
Finish();
}
}
OnRequestPermissionsResult 方法:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
switch (requestCode)
{
case REQUEST:
{
var permissionGranted = VerifyPermissions(grantResults);
if (permissionGranted)
{
TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
Util.UUID = tm.DeviceId;
StartActivity(typeof(MainActivity));
Finish();
}
else
{
this.ShowInformationWithClick(Resources.GetText(Resource.String.Alert), Resources.GetText(Resource.String.OK), "App requires storage, location, and phone access to function properly. Please grant necessary permissions. App will not work if permissions are not granted.", HandleClicked);
}
}
break;
}
}
VerifyPermissions 方法:
public static bool VerifyPermissions(Permission[] grantResults)
{
// At least one result must be checked.
if (grantResults.Length < 1)
return false;
// Verify that each required permission has been granted, otherwise return false.
foreach (Permission result in grantResults)
{
if (result != Permission.Granted)
{
return false;
}
}
return true;
}
我希望有一个 TL;DR: 版本,但我不知道问题出在哪里,因为我们的代码与该操作的其他示例代码几乎相似。我读了一些关于持久化 URI 和授予权限的内容;我尝试了其中的一些-它们没有用。并且没有完全理解一些 - this one 是其中之一。
谢谢。
【问题讨论】:
-
我在任何地方都看不到您为要共享的文件调用
GrantUriPermission。 -
@Cheesebaron - 根据文档,您实际上并不需要
GrantUriPermission方法。查看评论:GrantUriPermission Remarks -
@Cheesebaron - 但只是为了确保并尝试一下,我究竟应该在哪里调用该方法?
标签: android xamarin xamarin.android android-permissions