BitmapFactory can't decode a Bitmap from Uri after photos taken on Android Nougat

12,250

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.

Share:
12,250
SilentKnight
Author by

SilentKnight

Study the Major of Software Engineering in Wuhan University from Sep, 2009 to Jun, 2013. Android R&amp;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 &amp;&amp; Big Fun of Marvel Universe.

Updated on June 05, 2022

Comments

  • SilentKnight
    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 on onActivityResult:

    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 from BitmapFactory. 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
    SilentKnight about 7 years
    Yeah, finally, only BitmapFactory.decodeStream(inputStream) worked.
  • PGMacDesign
    PGMacDesign almost 7 years
    Just a heads up, you left out the code for the HelperFunctions.getDataColumn() function. This would not work without it.
  • carl
    carl over 4 years
    When 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!