Этот раздел обсуждался здесь по множеству вопросов, в основном с разными результатами и, благодаря изменениям API и различным типам URI, нет окончательного ответа.
У меня нет ответа сам, но поговорим об этом.
ExifInterface
имеет единственный конструктор, который принимает filePath
. Это само по себе раздражает, так как теперь не рекомендуется полагаться на пути - вам лучше использовать Uri
и ContentResolver
. OK.
Наш Uri
с именем Uri
может быть получен из намерения в onActivityResult
(если вы выбрали изображение из галереи с помощью ACTION_GET_CONTENT
) или может быть Uri
, который мы ранее имели (если вы выбираете изображение с камеры и вызов intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
).
API < 19
Наш Uri
может иметь две разные схемы:
- Урис, исходящий от камер, в основном имеет схему
file://
. Их довольно легко лечить, потому что они держат путь. Вы можете позвонитьnew ExifInterface(uri.getPath())
, и все будет готово. - Урис из галереи или других поставщиков контента обычно имеет интерфейс
content://
. Я лично не знаю, что это такое, но сводит меня с ума.
Этот второй случай, насколько я понимаю, должен обрабатываться с помощью ContentResolver
, который вы можете получить с помощью Context.getContentResolver()
. Следующие работают со всеми приложениями, которые я тестировал, в любом случае:
public static ExifInterface getPictureData(Context context, Uri uri) {
String[] uriParts = uri.toString().split(":");
String path = null;
if (uriParts[0].equals("content")) {
// we can use ContentResolver.
// let’s query the DATA column which holds the path
String col = MediaStore.Images.ImageColumns.DATA;
Cursor c = context.getContentResolver().query(uri,
new String[]{col},
null, null, null);
if (c != null && c.moveToFirst()) {
path = c.getString(c.getColumnIndex(col));
c.close();
return new ExifInterface(path);
}
} else if (uriParts[0].equals("file")) {
// it easy to get the path
path = uri.getEncodedPath();
return new ExifInterface(path);
}
return null;
}
API19 +
Мои проблемы возникают из Kitkat вперед с content://
URI. Киткат представляет Storage Access Framework
(см. здесь) вместе с новым намерением ACTION_OPEN_DOCUMENT
и сборщиком платформ. Однако сказано, что
В Android 4.4 и выше у вас есть дополнительная возможность использовать ACTION_OPEN_DOCUMENT, который отображает пользовательский интерфейс выбора, контролируемый система, которая позволяет пользователю просматривать все файлы, которые другие приложения стали доступными. Из этого единого пользовательского интерфейса пользователь может выбрать файл из любого поддерживаемого приложения.
ACTION_OPEN_DOCUMENT не предназначен для замены ACTION_GET_CONTENT. Тот, который вы должны использовать, зависит от потребностей ваше приложение.
Итак, чтобы это было очень просто, скажем, что мы в порядке со старым ACTION_GET_CONTENT
: он запустит диалог выбора, где вы можете выбрать приложение галереи.
Однако подход к контенту больше не работает. Иногда он работает на Kitkat, но никогда не работает на Lollipop, например. Я не знаю, что именно изменилось.
Я много искал и много пробовал; Другой подход, применяемый к Kitkat, заключается в следующем:
String wholeId = DocumentsContract.getDocumentId(uri);
String[] parts = wholeId.split(":");
String numberId = parts[1];
Cursor c = context.getContentResolver().query(
// why external and not internal ?
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{ col },
MediaStore.Images.Media._ID + "=?",
new String[]{ numberId },
null);
Это работает иногда, а другие нет. В частности, он работает, когда wholeId
является чем-то вроде image:2839
, но, очевидно, ломается, когда wholeId
является просто числом.
Вы можете попробовать это, используя системный сборщик (т.е. запустив галерею с помощью ACTION_OPEN_DOCUMENT
): если вы выберете изображение из "Редентс", оно работает; если вы выберете изображение из "Загрузки", он сломается.
Итак, как?!
Непосредственный ответ Вы не, вы не найдете пути к файлу из uris содержания в более новой версии ОС. Можно сказать, что не все содержимое uris указывает на изображения или даже файлы.
Это полностью нормально для меня, и сначала я старался избегать этого. Но тогда Как мы должны использовать класс ExifInterface, если мы не должны использовать пути?
Я не понимаю, как это делают современные приложения - поиск ориентации и метаданных - это проблема, с которой вы сразу сталкиваетесь, а ContentResolver
не предлагает API в этом смысле. У вас есть ContentResolver.openFileDescriptor()
и подобные вещи, но нет API-интерфейсов для чтения метаданных (которые действительно находятся в этом файле). Могут быть внешние библиотеки, которые читают Exif
материал из потока, но Im задается вопросом об общем/платформенном способе решения этой проблемы.
Я искал аналогичный код в приложениях с открытым исходным кодом в googles, но ничего не нашел.