注意:我经历了有关类似异常的其他问题。这不是重复的-它们都不是关于选择PDF或此特定错误。
插上。
我们正在研究Xamarin.Android项目。有一个“附加文档” 活动。
当用户尝试从下载目录中选择PDF时,它将引发以下异常:
Java.Lang.SecurityException:权限被拒绝:正在读取 com.android.providers.downloads.DownloadStorageProvider uri 内容://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-棉花糖)及更高版本。
权限字段和数组:
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是其中之一。
谢谢。
答案 0 :(得分:-1)
例外情况很明显。您需要在声明此活动的android:exported="true"
文件中设置AndroidManifest.xml
。目前设置为false。
尝试以下
<application android:label="JandB" android:icon="@drawable/icon">
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.jnbmedical.portal.fileprovider"
android:exported="true" <!-- change here -->
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
</application>