Difference between .getPath() vs cursor in getting the real path of a file from uri in Android

The main idea of the question is just same as the title - what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?

In case you don't get what I meant by using cursor, the example is here.

private String getRealPathFromURI(Uri contentURI) {
    String result;
    Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
    if (cursor == null) { // Source is Dropbox or other similar local file path
        result = contentURI.getPath();
    } else { 
        cursor.moveToFirst(); 
        int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}

The two most frequent ways were these two, but it seems a bit too complicated using cursor, while you can get the same result with one simple method, .getPath(). So, I think there must be the reason I should use the cursor in some cases, but I can't get it.

Could you explain me what it would be?


Solution 1:

what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?

A Uri is not a file. There is no "real path".

If the scheme of the Uri is file, then it represents a file on the filesystem that, in theory, your app should be able to access. Use getPath() to get the filesystem path.

If the scheme is anything else, it does not necessarily represent a file on the filesystem that your app can access. For example, if the scheme is http or https, the Uri represents something that would be downloaded from a Web server.

If the scheme is content, then it is backed by a ContentProvider. Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.

If the scheme is content and you specifically obtained the Uri from the MediaStore, then perhaps your Cursor approach will give you a path. It also might give you null, and the path that you get may not be accessible to you (just because the system's MediaStore can index a file does not imply that your app has access to that same file). This is worse on Android 10, where you do not have read access to external storage by default. Hence, this technique is unreliable and should not be used.

Beyond that, though, you cannot make any assumptions about what data is used to support that content Uri. It could be:

  • A local file on external storage
  • A local file on internal storage for the other app (e.g., served by FileProvider)
  • A local file on removable storage
  • A local file that is encrypted and needs to be decrypted on the fly by the ContentProvider
  • A stream of bytes held in a BLOB column in a database that needs to be served by the ContentProvider
  • A piece of content that needs to be downloaded by the other app first (e.g., Dropbox)
  • ...and so on

So, to recap: a Uri is not a file. There is no "real path".