Android - Невозможно открыть контент: file:///storage/emulated/0

У меня есть виджет с фоновым изображением из девяти патчей. Изображение было сохранено в / sdcard / mydir / bgs.

Когда я пытаюсь загрузить изображение с помощью метода setImageViewUri, у меня появляется эта ошибка:

Unable to open content: file:///storage/emulated/0/sdcard/mydir/bgs

..

затем

...

open failed: EACCES (Permission denied)

Это появляется только на главном экране и только с Nexus 10 и Nexus 7 (в последней версии 4.4 этой ошибки не существует). У меня также есть некоторые RemoteViews в моем приложении, и все работает правильно.

Я также добавил в манифест либо READ_EXTERNAL_STORAGE, либо WRITE_EXTERNAL_STORAGE.

Как я могу решить?

ОБНОВЛЕНИЕ: я проверил метод setImageViewUri и обнаружил, что он меняет путь моего файла.

if (value != null) {
            // Resolve any filesystem path before sending remotely
            value = value.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                value.checkFileUriExposed("RemoteViews.setUri()");
            }
        }

Этот метод получает мое значение (/sdcard/mydir/bgs) и изменяет его на (storage/emulated/0/sdcard/mydir/bgs). Но этот файл не существует в системе через ADB.

4 ответа

Решение

Кажется setImageViewUri больше не безопасно использовать с file:// uris.

Зачем?

Желе представило READ_EXTERNAL_STORAGE разрешение. Приложения, которые хотят читать из внешнего хранилища, должны иметь это разрешение. Это не было применено по умолчанию до KitKat.

Программа запуска не имеет этого разрешения. На самом деле, вам не гарантируется, что любой RemoteView, к которому вы подключаетесь, имеет это разрешение. Это делает небезопасным использование setImageViewUri, так как вы не знаете, сможет ли удаленный просмотр изображений даже прочитать данный URI.

Что теперь?

Вариант 1: использовать setImageViewBitmap.

Возможно, вы отошли от этой опции из-за неудачных операций связывания. Хитрость в том, чтобы убедиться, что ваш образ меньше 1 МБ. Это не так сложно, как кажется. Вы можете точно рассчитать, насколько большим будет ваше изображение. Например, если вы используете изображение ARGB_8888, это означает, что вам потребуется 4 байта на пиксель. Мы можем рассчитать максимальный размер:

1 МБ = 1048576 байт = 262144 пикселей = 512 x 512 изображений

Конечно, вы можете выжать из него больше, используя RGB_565, чтобы получить в 2 раза больше пикселей.

Также обратите внимание, что вам может не понадобиться огромное изображение, если ваш виджет маленький. Ваше приложение может спросить о его конкретных параметрах следующим образом:

Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetIds[i]);

int minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);

int maxWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int maxHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);

Просто имейте в виду, что возвращаемые значения представлены в виде пикселей, а не пикселей, поэтому вам необходимо преобразовать их для масштабирования растрового изображения.

Вариант 2. Использование контент-провайдера

Если по какой-то причине вам все еще не нравятся ограничения IPC, вы всегда можете создать собственного поставщика контента. Передайте этот URI в setImageViewUri, и все будет хорошо.

Как насчет переключения пути?

Переключатель пути не является реальной проблемой. Похоже, это проблема, но эмуляция на самом деле работает нормально. Попробуйте создать файл внутри //storage/emulated/0/sdcard/mydir/bgs и файл создаст просто отлично.

Вы заметите, что в то время как исключением является FileNotFoundException, сообщение "Отказано в доступе".

По сравнению с Lollipop, Google представил новый способ явного предоставления приложениям разрешения, которое вы хотите, чтобы они использовали на вашем устройстве, и отключение тех, которые вы хотите запретить приложению. Если вы заметили, в вашем мониторе Android, журнал показывает

java.io.FileNotFoundException: /storage/emulated/0/advert.mp4: open failed: EACCES (Permission denied)

EACCESS permission denied является причиной печально известного Java FileNotFoundException,

Чтобы решить эту проблему, просто перейдите к разрешению вашего приложения и включите хранилище для вашего приложения.

Тебе нужно

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

в вашем манифесте.

Я думаю, что вы вводите путь SD-карты вручную. Вместо того, чтобы вручную вводить путь к SD-карте, вы должны получить путь к хранилищу следующим образом:

File extStore = Environment.getExternalStorageDirectory();
String mPath = extStore.getAbsolutePath() + "/mydir/bgs";
Другие вопросы по тегам