简体   繁体   中英

Image uri doesn't display images on ImageView on some android device

I have an ImageView. When you click on a ImageView, it will open gallery and you pick up a image and show it on ImageView. I have a condition that when I close my app and then I open it, the image will retain there . So for that, I am storing image Uri on sharedprefrence. And on opening the application i am retrieving the same Uri and tries to display the image on imageView.

However in some phones - image appears perfect like Mi(Lollipop), Samsung(KitKat) but it doesn't appear in phones like - Motorola(Marshmallow) ,One Plus One (Marshmallow). Any idea why it's happening ? Here is my code

For picking up an image i am using

Intent intent=new Intent();intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE);

And on OnActivityResult() code is

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);

     if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data) {
         Uri uri=data.getData();
         Picasso.with(UsersProfileActivity.this)
             .load(uri)
             .centerCrop()
             .resize(200,200)
             .into(img_photo);

         // This profile image i am storing into a sharedpreference        
         profile_image=uri.toString();
     }

And while retrieving from sharedprefrence I convert String to uri by using Uri.parse(profile_image)

However if I notice, uri returns for different android phone is as follows

Mi(Lollipop)- Uri=content://media/external/images/media/12415
samsung(KitKat)-Uri=content://media/external/images/media/786
Motorola(Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A30731
One Plus One (Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A475

Hence when uri content is -content://media/external/images/media/ is display image on ImageView Perfect and in other cases it is not

Try this

I developed and tested and working on

  1. Lenovo K3 Note(Marshmallow)
  2. Motorola(Lollipop)
  3. Samsung(KitKat)

In your MainActivity.java add

    profilePicture = (ImageView)findViewById(R.id.imgProfilePicture);
    sharedPreferences = getSharedPreferences(getString(R.string.app_name)+"_ProfileDetails",MODE_PRIVATE);

    // get uri from shared preference
    String profilePicUri = sharedPreferences.getString("profilePicUrl","");

    // if uri not found
    if (profilePicUri.equalsIgnoreCase("")){
        // code for open gallery to select image
        Intent intent;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        }else{
            intent = new Intent(Intent.ACTION_GET_CONTENT);
        }
        intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setType("image/*");

        startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE_CONSTANT);
    }else{
        try{
            final int takeFlags =  (Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            // Check for the freshest data.
            getContentResolver().takePersistableUriPermission(Uri.parse(profilePicUri), takeFlags);
            // convert uri to bitmap
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.parse(profilePicUri));
            // set bitmap to imageview
            profilePicture.setImageBitmap(bitmap);
        }
        catch (Exception e){
            //handle exception
            e.printStackTrace();
        }
    }

Now Handle onActivityResult .

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SELECT_PICTURE_CONSTANT && resultCode == RESULT_OK && null != data) {
        Uri uri = data.getData();
        // This profile image i am storing into a sharedpreference
        SharedPreferences.Editor editor = sharedPreferences.edit();
        // save uri to shared preference
        editor.putString("profilePicUrl",uri.toString());
        editor.commit();
        profilePicture.setImageURI(uri);
    }
}

In your Manifest file add permission

<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Note:

Assuming you added code for take permission for Marshmallow

Result Uri

lenovo K3 Note: content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG_20160606_212815.jpg

samsung: content://com.android.providers.media.documents/document/image%3A2086

motorola: content://com.android.providers.media.documents/document/image%3A15828

I think the problem with the uri of the image file, I am using a class for getting exact URI, and it's tested and working on all versions.

package fandooo.com.fandooo.util;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class ImageFilePath {

    /**
     * Method for return file path of Gallery image
     * 
     * @param context
     * @param uri
     * @return path of the selected image file from gallery
     */
    public static String getPath(final Context context, final Uri uri) {

        // check here to KITKAT or new version
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                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];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"),
                        Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (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 getDataColumn(context, contentUri, selection,
                        selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     * 
     * @param context
     *            The context.
     * @param uri
     *            The Uri to query.
     * @param selection
     *            (Optional) Filter used in the query.
     * @param selectionArgs
     *            (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri,
                                       String selection, String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };

        try {
            cursor = context.getContentResolver().query(uri, projection,
                    selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * @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());
    }
}

Use getPath to find the exact path of a file. It's simplest way to handle problem like you have defined. Please try and let me know if you need further assistance :)

Quoting from 4.4 API overview :

'On previous versions of Android, if you want your app to retrieve a specific type of file from another app, it must invoke an intent with the ACTION_GET_CONTENT action. This action is still the appropriate way to request a file that you want to import into your app. However, Android 4.4 introduces the ACTION_OPEN_DOCUMENT action, which allows the user to select a file of a specific type and grant your app long-term read access to that file (possibly with write access) without importing the file to your app.'(emphasis added)

To allow your app to retrieve previously-selected images, just change the action from ACTION_GET_CONTENT to ACTION_OPEN_DOCUMENT (solution confirmed working on Nexus 7 2013).

    Intent intent = new Intent();
    intent.setType("image/*");
    String action = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT;
    intent.setAction(action);
    startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE);

Pass your URI in this method it will return a String and than convert it into Bitmap

  String path = getPath(getApplicationContext(), uri);
  Bitmap bitmap = BitmapFactory.decodeFile(path);
  imageView.setImageBitmap(bitmap);





 public static String getPath(final Context context, final Uri uri) {

    // DocumentProvider
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {

        if (isExternalStorageDocument(uri)) {// ExternalStorageProvider
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            String storageDefinition;


            if ("primary".equalsIgnoreCase(type)) {

                return Environment.getExternalStorageDirectory() + "/" + split[1];

            } else {

                if (Environment.isExternalStorageRemovable()) {
                    storageDefinition = "EXTERNAL_STORAGE";

                } else {
                    storageDefinition = "SECONDARY_STORAGE";
                }

                return System.getenv(storageDefinition) + "/" + split[1];
            }

        } else if (isDownloadsDocument(uri)) {// DownloadsProvider

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);

        } else if (isMediaDocument(uri)) {// MediaProvider
            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 getDataColumn(context, contentUri, selection, selectionArgs);
        }

    } else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);

    } else if ("file".equalsIgnoreCase(uri.getScheme())) {// File
        return uri.getPath();
    }

    return null;
}

You need to request the permission to read external storage to user at runtime for Android 6+

https://developer.android.com/training/permissions/requesting.html#perm-check

Hope this helps.

Maybe you can handle it by converting URI to real path first.

public String getRealPathFromURI(Context context, Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = { MediaStore.Images.Media.DATA };
        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);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

And your onActivityResult should looks like this

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data)
    {
        Uri uri=data.getData();
        String imagePath = getRealPathFromURI(getApplicationContext(), uril); 
Picasso.with(UsersProfileActivity.this).load(imagePath).centerCrop().resize(200,200).into(img_photo);

     profile_image = imagePath;  

    }

I have managed to do this in the past. Instead of storing it in SharePreferences, I save it in the Bundle when onSaveInstanceState(Bundle) get called by: bundle.putParcelable(SOME_KEY, uri). You can do this because Uri implements Parcelable. After that, check for this uri in the Bundle passed in onCreate(). Code:

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null){
        loadImage((Uri)saveInstanceState.getParcelable(SOME_KEY))
    }
}

public void onSaveInstanceState(Bundle bundle){
     bundle.putParcelable(SOME_KEY, uri)
}

P/S: I guess the problem of storing the uri in SharePreference is encoding/decoding Uri.

You can Easily convert URI to Bitmap:

ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(selectedFileUri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);


        parcelFileDescriptor.close();

And then set in to Imageview: imgview.setImageBitmap(image);

Hope this will help you.

Could be a Picasso versioning issue. They fixed something with loading large images in version 2.5.0 (2.4.0...?), which isn't available via the repositories yet.

You'll need to manually include the nightly snapshot of Picasso, and that will likely fix your issue. It did for me.

I also run into something like this earlier and found a useful helper class which gives you the file's exact location. Here is the link .

You can use the methods like FileUtils.getPath(context, uri) or FileUtils.getFile(context, uri) to get the file. Then just use it like this:

 Picasso.with(UsersProfileActivity.this)
         .load(Uri.fromFile(file))
         .centerCrop()
         .resize(200,200)
         .into(img_photo);

Hope this helps.

Just not 100% sure if I get the question. If certain images cannot be shown in the imageView (even before closing and restarting the app) then the issue is with Picasso is being able to handle the contents from certain providers. I am not getting the sense that this is the case, but if it is, then you might want to consider filing a bug/RFE for that library. On the other hand, if Picasso is correctly showing the image for a given Uri, but not after Uri_org -> String -> Uri_regen conversion then it means that Uri_org is not the same as Uri_regen. You can (temporarily) add a couple of lines your code to convert the String back to Uri and put a breakpoint just after and compare the two Uri's. That should give you a clue as to how things are going wrong.

profile_image=uri.toString();
//add the next few lines for debugging purposes only; remove them later
Uri uri2 = Uri.parse(profile_image)
if (!uri.equals(uri2)
   //add a breakpoint on the next line
   Log.d("Uri mismatch), "Ouch");
//remove the lines after you are done with debugging

If you are not hitting the breakpoint, then move the breakpoint back to the "if" statement and visually check the two Uri's (which almost certainly will be the same, otherwise you would have had hit the breakpoint, but this would help checking the debug code itself). If the Uri's are the same then somehow the String preference is not being preserved correctly for some reason. In that case make a note of the original Uri and add a breakpoint on the line that converts the String back to Uri:

Uri uri = Uri.parse(profile_image) //profile_image is retrieved from sharedpreferences

If the previous experiment worked, it should be working again here unless the profile_image is different. Either way you should get pointers as to where the issue is.

Good luck!

A simple fix can be just add "file://" to the beginning of the file path. For example: changing {{uri: "/path/to/file"}} to {{uri: "file:///path/to/file"}} works for any platform.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM