当我尝试访问先前存储在SharedPreferences中的照片URI时,我的应用仅在android> = 6.0(marshmallow)中崩溃。虽然图像是第一次检索而没有任何错误。我正在使用targetSdkVersion
22,因此我无需在API> = 23中处理运行时权限。
我的清单文件中的权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
我的图像如下:
// Determine Uri of camera image to save.
final File root = new File(Environment.getExternalStorageDirectory() + File.separator + "pics" + File.separator);
root.mkdirs();
final String fname = Calendar.getInstance().getTimeInMillis() + ".jpg";
final File sdImageMainDirectory = new File(root, fname);
outputFileUri = Uri.fromFile(sdImageMainDirectory);
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for (ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
cameraIntents.add(intent);
}
// Filesystem.
Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[cameraIntents.size()]));
startActivityForResult(chooserIntent, Constants.IMAGE_PICKER);
这是我的OnActivityResult方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == Constants.IMAGE_PICKER) {
if (data == null) {
selectedImageUri = outputFileUri;
} else {
selectedImageUri = data.getData();
}
if (selectedImageUri != null) {
try {
String path = AppUtils.getPath(this, selectedImageUri);
if (path != null) {
Bitmap bitmap = AppUtils.decodeFile(new File(path));
File f = new File(System.currentTimeMillis() + ".jpg");
OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
uploadImageToServer(f, bitmap);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
其中getPath()是来自https://stackoverflow.com/a/20559418/3758972的函数。
stacktrace:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.badiyajobs.app/com.badiyajobs.app.screens.ProfileActivity}: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider from ProcessRecord{25e87b7 20432:com.badiyajobs.app/u0a389} (pid=20432, uid=10389) that is not exported from uid 10107
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2426)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:154)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider from ProcessRecord{25e87b7 20432:com.badiyajobs.app/u0a389} (pid=20432, uid=10389) that is not exported from uid 10107
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:3605)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:4799)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2018)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1466)
at android.content.ContentResolver.query(ContentResolver.java:475)
at android.content.ContentResolver.query(ContentResolver.java:434)
at com.badiyajobs.app.utils.AppUtils.getDataColumn(AppUtils.java:790)
at com.badiyajobs.app.utils.AppUtils.getPath(AppUtils.java:776)
at com.badiyajobs.app.screens.ProfileActivity.setDefaultValue(ProfileActivity.java:192)
at com.badiyajobs.app.screens.ProfileActivity.onCreate(ProfileActivity.java:162)
at android.app.Activity.performCreate(Activity.java:6259)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1130)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2379)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:154)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
我不知道我错过了什么。请帮忙。
答案 0 :(得分:6)
在您的意图中使用ACTION_OPEN_DOCUMENT而不是ACTION_PICK。它允许对您请求的文件进行长期,持久的访问。
Intent galleryIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
此处的文档: https://developer.android.com/guide/topics/providers/document-provider.html
答案 1 :(得分:1)
改善@Ashton Coulson的答案,你可以处理Kitkat版本来使用这个参数:
String action;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
action = Intent.ACTION_OPEN_DOCUMENT;
} else {
action = Intent.ACTION_PICK;
}
Intent intent = new Intent(action, uri);
需要if,因为如documentation中所述,存储访问框架(SAF)是在Android 4.4(API级别19)中引入的。
答案 2 :(得分:1)
按如下所示创建类名称RealPathUtil
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
public class RealPathUtil {
@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(Context context, Uri uri){
String filePath = "";
String wholeID = DocumentsContract.getDocumentId(uri);
// Split at colon, use second item in the array
String id = wholeID.split(":")[1];
String[] column = { MediaStore.Images.Media.DATA };
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[]{ id }, null);
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
}
cursor.close();
return filePath;
}
@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
String result = null;
CursorLoader cursorLoader = new CursorLoader(
context,
contentUri, proj, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
if(cursor != null){
int column_index =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
}
return result;
}
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index
= cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
}
然后,您可以像这样在onActivityResult
中获得真实路径
String realPath;
// SDK < API11
if (Build.VERSION.SDK_INT < 11)
realPath = RealPathUtil.getRealPathFromURI_BelowAPI11(this, data.getData());
// SDK >= 11 && SDK < 19
else if (Build.VERSION.SDK_INT < 19)
realPath = RealPathUtil.getRealPathFromURI_API11to18(this, data.getData());
// SDK > 19 (Android 4.4)
else
realPath = RealPathUtil.getRealPathFromURI_API19(this, data.getData());
盐浆
content://com.android.providers.media.documents/document/image:36
到
/storage/emulated/0/Pictures/JPEG_20190812_163204_299063245992150918.jpg
希望这很有帮助!!
答案 3 :(得分:0)
你应该持续获得uri许可:
Uri uri = intent.getData();
context.getContentResolver().takePersistableUriPermission(uri
, intent.getFlags()
& ( Intent.FLAG_GRANT_READ_URI_PERMISSION
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
);
答案 4 :(得分:0)
你可以添加这一行:
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION);