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

SilentKnight picture SilentKnight · Feb 16, 2017 · Viewed 10.9k times · Source

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?

Answer

Raghunandan picture Raghunandan · Apr 24, 2017

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