Как обрабатывать SAF, когда я могу обрабатывать только файл или путь к файлу?

Фон

Вплоть до Android Q, если мы хотели получить информацию о файле APK, мы могли использовать WRITE_EXTERNAL_STORAGE и READ_EXTERNAL_STORAGE, чтобы получить доступ к хранилищу, а затем использовать функцию PackageManager.getPackageArchiveInfo в пути к файлу.

Существуют аналогичные случаи, такие как использование класса ZipFile для сжатого файла, и, вероятно, бесчисленное множество API-интерфейсов и сторонних библиотек.

Эта проблема

Google недавно объявил об огромном количестве ограничений на Android Q.

Один из них называется Scoped Storage, который нарушает права доступа к хранилищу, когда дело доходит до доступа ко всем файлам, имеющимся на устройстве. Он позволяет вам обрабатывать медиа-файлы или использовать очень ограниченную Storage-Access-Framework ( SAF), которая не позволяет приложениям получать доступ к файлам и использовать их с помощью File API и путей к файлам.

Когда была выпущена Android Q Beta 2, из-за этого она сломала множество приложений, в том числе Google. Причина заключалась в том, что он был включен по умолчанию, затрагивая все приложения, независимо от того, нацелены они на Android Q или нет.

Причина в том, что многие приложения, SDK и сама платформа Android - все они используют File API довольно часто. Во многих случаях они также не поддерживают InputStream или решения, связанные с SAF. Примером этого является именно пример парсинга APK, о котором я писал (PackageManager.getPackageArchiveInfo).

В Q beta 3, однако, все немного изменилось, так что приложение, предназначенное для Q, будет иметь объемное хранилище, и есть флаг, чтобы отключить его и при этом использовать обычные разрешения для хранилища и File API как обычно. К сожалению, флаг является только временным (читай здесь), поэтому он задерживает неизбежное.

Что я пробовал

Я попробовал и нашел следующие вещи:

  1. Использование разрешения на хранение действительно не позволило мне прочитать любой файл, который не является медиа-файлом (я хотел найти APK-файлы). Как будто файлы не существуют.

  2. Используя SAF, я смог найти APK-файл и, найдя обходной путь, чтобы найти его реальный путь (ссылка здесь), заметил, что File API может сказать мне, что файл действительно существует, но он не может получить его размер, и фреймворк не смог использовать свой путь, используя getPackageArchiveInfo, Написал об этом здесь

  3. Я попытался сделать символическую ссылку на файл (ссылка здесь), а затем прочитать из символической ссылки. Это не помогло.

  4. В случае разбора APK-файлов я попытался найти альтернативные решения. Я нашел 2 репозитория github, которые обрабатывают APK, используя класс File ( здесь и здесь), и тот, который использует InputStream ( здесь). К сожалению, тот, который использует InputStream, очень старый, в нем отсутствуют различные функции (например, получение имени и значка приложения), и он не будет обновляться в ближайшее время. Кроме того, наличие библиотеки требует обслуживания, чтобы не отставать от будущих версий Android, в противном случае у нее могут возникнуть проблемы в будущем или даже сбой.

Вопросы

  1. Вообще, есть ли способ по-прежнему использовать File API при использовании SAF? Я не говорю о корневых решениях или просто копирую файл куда-то еще. Я говорю о более твердом решении.

  2. В случае синтаксического анализа APK, есть ли способ преодолеть эту проблему, что платформа предоставляет только путь к файлу в качестве параметра? Любой обходной путь или способ использования InputStream возможно?

0 ответов

Как обрабатывать SAF, когда я могу обрабатывать только файл или путь к файлу? Это возможно, даже если вы можете отправить только объект Java File или строку пути к библиотечной функции, которую вы не можете изменить:

Сначала получите Uri для файла, который необходимо обработать (в строковой форме это будет похоже на "content://..."), затем:

    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(uri, "r"); // may get FileNotFoundException here
        // Obtain file descriptor:
        int fd = parcelFileDescriptor.getFd(); // or detachFd() if we want to close file in native code
        String linkFileName = "/proc/self/fd/" + fd;
        // Call library function with path/file string:
        someFunc(/*file name*/ linkFileName);
        // or with File parameter
        otherFunc(new File(linkFileName));
        // Finally, if you did not call detachFd() to obtain the file descriptor, call:
        parcelFileDescriptor.close();
        // Otherwise your library function should close file/stream...
    } catch (FileNotFoundException fnf) {
        fnf.printStackTrace(); // or whatever
    }          

Публикуем еще один ответ, чтобы было больше места, и я мог вставить фрагменты кода. Учитывая дескриптор файла, как описано в моем предыдущем ответе, я попытался использовать пакет net.dongliu:apk-parser, упомянутый @androiddeveloper в исходном вопросе, следующим образом (Lt.d - это мое сокращение использования Log.d(SOME_TAG, string...)):

            String linkFileName = "/proc/self/fd/" + fd;
            try (ApkFile apkFile = new ApkFile(new File(linkFileName))) {
                ApkMeta apkMeta = apkFile.getApkMeta();
                Lt.d("ApkFile Label: ", apkMeta.getLabel());
                Lt.d("ApkFile pkg name: ", apkMeta.getPackageName());
                Lt.d("ApkFile version code: ", apkMeta.getVersionCode());
                String iconStr = apkMeta.getIcon();
                Lt.d("ApkFile icon str: ", iconStr);
                for (UseFeature feature : apkMeta.getUsesFeatures()) {
                    Lt.d(feature.getName());
                }
            }
            catch (Exception ex) {
                Lt.e("Exception in ApkFile code: ", ex);
                ex.printStackTrace();
            }
        }

Он дает мне правильную метку приложения, для значка он дает мне только строку для каталога ресурсов (например, "res/drawable-mdpi-v4/fex.png"), поэтому снова должны быть применены необработанные функции чтения ZIP к прочитать фактические биты значка. В частности, я тестировал APK ES File Explorer Pro (купил этот продукт и сохранил APK для собственной резервной копии, получил следующий результат:

 I/StorageTest: ApkFile Label: ES File Explorer Pro
 I/StorageTest: ApkFile pkg name: com.estrongs.android.pop.pro
 I/StorageTest: ApkFile version code: 1010
 I/StorageTest: ApkFile icon str: res/drawable-mdpi-v4/fex.png
 I/StorageTest: android.hardware.bluetooth
 I/StorageTest: android.hardware.touchscreen
 I/StorageTest: android.hardware.wifi
 I/StorageTest: android.software.leanback
 I/StorageTest: android.hardware.screen.portrait
Другие вопросы по тегам