BitmapFactory can't decode a Bitmap from Uri after photos taken on Android Nougat
Solution 1
I tried your code. Here's the sample of my try.https://github.com/raghunandankavi2010/SamplesAndroid/tree/master/StackOverFlowTest.
Have a look at this blog https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
In the blog commonsware mentions that you should not do new File (mPicPath.getPath())
.
Instead you should use the below in onActivityResult
try {
InputStream ims = getContentResolver().openInputStream(mPicPath);
// just display image in imageview
imageView.setImageBitmap(BitmapFactory.decodeStream(ims));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
And the xml
<external-files-path name="external_files" path="path" />
Note: its a content uri that you have. On my phone i get the uri as below. Tested only on Nexus6p.
content://com.example.raghu.stackoverflowtest.fileProvider/external_files/Pictures/JPEG_20170424_161429691143693160.jpg
More on file provider https://developer.android.com/reference/android/support/v4/content/FileProvider.html
Solution 2
Try this function may be you get path from it. This is works for me
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getRealPathFromURI_API19(Context context, Uri uri) {
if (HelperFunctions.isExternalStorageDocument(uri)) {
// ExternalStorageProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
} else if (HelperFunctions.isDownloadsDocument(uri)) {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return HelperFunctions.getDataColumn(context, contentUri, null, null);
} else if (HelperFunctions.isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return HelperFunctions.getDataColumn(context, contentUri, selection,
selectionArgs);
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (HelperFunctions.isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return HelperFunctions.getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
here is my static function HelperFunction class which use above.
public class HelperFunction{
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
}
Solution 3
It looks very much like {@applicationID} is actually supposed to contain the package id of your application. As it is, the folder doesn't exist so the file can't be written or read. it looks like SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES); is not returning a valid path.
Solution 4
Nougat and Previous Update Is Working With This Code
To get the real path from database in nougat use this function and pass the uri which you are getting in data field as in onActivityResult and then get the file from the path.
public String getPath(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
String document_id = cursor.getString(0);
document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
cursor.close();
cursor = getContentResolver().query(
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
Solution 5
You should use FileProvider
.
You may refer to this commit for required change.
![SilentKnight](https://i.stack.imgur.com/wnKPl.png?s=256&g=1)
SilentKnight
Study the Major of Software Engineering in Wuhan University from Sep, 2009 to Jun, 2013. Android R&D Engineer with the Hero Complex @ Big Data Innovation Centre, CreditEase. Google, or Android Technology Enthusiast exactly. Hollywood Movies, Grammy Music, Basketball Games Lover. English Language and Literature Lover. Loyal Follower of DC Heroes && Big Fun of Marvel Universe.
Updated on June 05, 2022Comments
-
SilentKnight about 2 years
I tried to take a photo and then to use the photo. Here is what I did.
My device was Nexus 6P (Android 7.1.1).
First, I created a
Uri
:Uri mPicPath = UriUtil.fromFile(this, UriUtil.createTmpFileForPic()); //Uri mPicPath = UriUtil.fromFile(this, UriUtil.createFileForPic());
And then, I started
Intent
:Intent intent = ActivityUtils.getTakePicIntent(mPicPath); if (intent.resolveActivity(getPackageManager()) != null) { startActivityForResult(intent, RequestCode.TAKE_PIC); }
At last, I handled this
Uri
ononActivityResult
:if (requestCode == RequestCode.TAKE_PIC) { if (resultCode == RESULT_OK && mPicPath != null) { Bitmap requireBitmap = BitmapFactory.decodeFile(mPicPath.getPath()); //path is like this: /Download/Android/data/{@applicationId}/files/Pictures/JPEG_20170216_173121268719051242.jpg requireBitmap.recycle();//Here NPE was thrown. } }
At the meantime, Here are
UriUtil
:public class UriUtil { public static File createFileForPic() throws IOException { String fileName = "JPEG_" + new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault()).format(new Date()) + ".jpg"; File storageDic = SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES); return new File(storageDic, fileName); } public static File createTmpFileForPic() throws IOException { String fileName = "JPEG_" + new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault()).format(new Date()); File storageDic = SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile(fileName, ".jpg", storageDic); } public static Uri fromFile(@NonNull Context context, @NonNull File file) { if (context == null || file == null) { throw new RuntimeException("context or file can't be null"); } if (ActivityUtils.requireSDKInt(Build.VERSION_CODES.N)) { return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".file_provider", file); } else { return Uri.fromFile(file); } } }
and
getTakePicIntent(Uri)
:public static Intent getTakePicIntent(Uri mPicPath) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, mPicPath); if (!ActivityUtils.requireSDKInt(Build.VERSION_CODES.KITKAT_WATCH)) {//in pre-KitKat devices, manually grant uri permission. List<ResolveInfo> resInfoList = SPApplication.getInstance().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; SPApplication.getInstance().grantUriPermission(packageName, mPicPath, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } } else { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } return intent; }
and
requireSDKInt
:public static boolean requireSDKInt(int sdkInt) { return Build.VERSION.SDK_INT >= sdkInt; }
Everything worked on different Android API except on Android Nougat(7.x.x). Even 'FileProvider' was provided, 'requireBitmap' always be returned as 'null'.
After logs read,
FileNotFoundException
was thrown fromBitmapFactory
. It was like:BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /Download/Android/data/{@applicationId}/files/Pictures/JPEG_20170216_1744551601425984925.jpg (No such file or directory)
It seems all is clear, but I still can't understand that.
How could it be? Clearly I created a
File
! How could I solve that? Any ideas? -
SilentKnight about 7 yearsYeah, finally, only
BitmapFactory.decodeStream(inputStream)
worked. -
PGMacDesign almost 7 yearsJust a heads up, you left out the code for the HelperFunctions.getDataColumn() function. This would not work without it.
-
carl over 4 yearsWhen using a "content://..." path like that, I can load a chosen png file using Glide, whereas, using BitmapFactory.loadFile(uri), this class returns with a file not found expection, stating, ODDLY ENOUGH, that the path not found is "content:/…" with ONE forward slash only. Why is BitmapFactory removing one of the slashes, only to fail? Mystic mysterium!