I am receiving a FileNotFound exception when trying to turn an image into a Bitmap to scale the image down significantly.
I am retrieving an image the user selects from the gallery like so
public static void GET_PICTURE_FROM_GALLERY (Activity activity)
{
Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
getIntent.setType("image/*");
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickIntent.setType("image/*");
Intent chooserIntent = Intent.createChooser(getIntent, "Select Image");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {pickIntent});
activity.startActivityForResult(chooserIntent, REQUEST_SELECT_PHOTO);
}
Then, when the user is done picking:
public void setChosenPicture (Activity activity, Intent data)
{
if(editingViewHelper != null)
{
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Uri selectedImage = data.getData();
System.out.println("GOT URI: " + selectedImage.toString());
Cursor cursor = activity.getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
editingViewHelper.holder.tempUri = Uri.parse(cursor.getString(columnIndex));
System.out.println("SET URI: " + editingViewHelper.holder.tempUri.toString());
cursor.close();
editingViewHelper.holder.imageView.setImageURI(editingViewHelper.holder.tempUri);
}
}
This outputs the following:
GOT URI: content://media/external/images/media/110
SET URI: /storage/emulated/0/DIRECTORY/IMG_20170606_154512_1664512804.jpg
Now this works great, but only if I call imageView.setImageUri(uri); However, I cannot do that because I must scale the image down so the app doesn't use ridiculous amounts of ram. To do this, I have this method
public static Bitmap decodeUri(Context c, Uri uri, final int requiredSize)
{
System.out.println("URI: " + uri.toString());
try
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
//This line is where the error occurs.
BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o);
int width_tmp = o.outWidth;
int height_tmp = o.outHeight;
int scale = 1;
while(true)
{
if(width_tmp / 2 < requiredSize || height_tmp / 2 < requiredSize)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o2);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
return null;
}
}
And finally, I have the following .output FileNotFound exception
URI: /storage/emulated/0/DIRECTORY/IMG_20170606_154512_1664512804.jpg
java.io.FileNotFoundException: No content provider: /storage/emulated/0/DIRECTORY/IMG_20170606_154512_1664512804.jpg
Now the weird thing is that this method does work but only if it is passed the Uri from a picture taken in my app. Here is an example output of the last method that does not throw any errors
Uri: content://com.subliroid.dinnerdecider.provider/external_files/Pictures/DIRECTORY/IMG_20170606_154512_1664512804.jpg
I am able to call imageView.setImageUri(uri) with BOTH types of uris, but I cannot scale the one type down. I was able to ignore this until I began testing my app on real devices that take 4k+ pictures compared to the small ones the emulator takes.
The simplest thing to do, by far, is to delete most of this code and use an existing image-loading library. Google recommends Glide; I have used Picasso, and there are plenty of others.
Then, when the user is done picking:
The query()
call will fail in many situations (images on removable storage, users choosing apps that you might not expect in response to your Intent
, etc.).
And finally, I have the following .output FileNotFound exception
You are unnecessarily creating a new Uri
, and you are doing so incorrectly, as the value you are passing into Uri.parse()
does not have a scheme.
If you are not going to use existing, already-debugged code (i.e., image-loading libraries), pass in the Uri
you were given in onActivityResult()
to your bitmap-scaling code. Then, figure out when you are going to move most of this logic off of the main application thread, as you are freezing your UI in your current implementation.