Расположение файла расширения Google Play

У нас есть большой файл расширения ~ 600 МБ, который мы получаем с помощью механизма Google Play Expansion APK. Мы также должны разархивировать этот файл для фактического использования. Таким образом, для всего приложения и данных требуется около 1,4 ГБ памяти.

Насколько я могу судить, Google Play настаивает на загрузке .obb на «внутреннюю» SD-карту, и, похоже, нет никакого способа изменить это. У нас много пользователей, у которых много свободного места на «внешней» SD-карте, но мало места на внутренней. Они кричат, что приложение занимает так много места. Можем ли мы что-нибудь с этим сделать?

В настоящее время мы расширяем файл .obb до:

getExternalStorageDirectory()/Android/данные

Я полагаю, мы могли бы спросить пользователя, где он хочет, и он мог бы выбрать настоящую внешнюю SD-карту. Однако это все равно оставит (в основном бесполезный) файл .obb на внутренней SD-карте, и Google Play говорит, что мы не можем его удалить.

Есть ли у кого-нибудь предложения о том, как правильно с этим справиться?


person btschumy    schedule 08.03.2013    source источник
comment
У нас много пользователей, у которых много свободного места на внешней SD-карте, но мало места на внутренней — с точки зрения Android такого понятия нет. Существует только одно внешнее хранилище, представленное getExternalStorageDirectory(). Все, что выходит за рамки этого, связано с расширениями производителя устройств для Android, о которых APK-файл Play Expansion ничего не знает.   -  person CommonsWare    schedule 09.03.2013


Ответы (3)


Согласно документации о расположении хранилища файлов расширения, файлы расширения хранятся в

<shared-storage>/Android/obb/<package-name>/

где shared-storage — это то, что возвращает getExternalStorageDirectory(), что должно быть на SD-карте для пользователей, у которых есть SD-карта.

К сожалению, как указано на той же странице:

Чтобы обеспечить правильное поведение, вы не должны удалять, перемещать или переименовывать файлы расширения.

Я бы обратил особое внимание на этот абзац:

Если вам необходимо распаковать содержимое ваших файлов расширения, не удаляйте файлы расширения .obb впоследствии и не сохраняйте распакованные данные в том же каталоге. Вы должны сохранить распакованные файлы в каталоге, указанном getExternalFilesDir(). Однако, если возможно, лучше использовать формат файла расширения, который позволяет читать непосредственно из файла, а не распаковывать данные. Например, мы предоставили проект библиотеки под названием APK Expansion Zip Library, который считывает ваши данные непосредственно из ZIP-файла.

person ianhanniballake    schedule 09.03.2013
comment
Спасибо за ответ. Проблема в том, что для многих устройств есть 2 SD-карты. Внутренний и внешний (съемный). Кажется, что getExternalStorageDirectory() всегда возвращает внутреннюю SD-карту, которая обычно имеет более ограниченное хранилище. У нас должен быть очень быстрый доступ к данным в файле .obb (и из кода C), поэтому мы должны расширить его. - person btschumy; 09.03.2013

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

Например, путь, по которому хранятся файлы OBB, указан как com.google.android.vending.expansion.downloader.Helpers:

static public String getSaveFilePath(Context c) {
    File root = Environment.getExternalStorageDirectory();
    String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
    return path;
}

(Также необходимо изменить getFilesystemRoot(String path) и isFilenameValid(String filename))

Если вы также используете библиотеку zip-файлов расширения Google, вам также необходимо изменить 3 метода в com.android.vending.expansion.zipfile.APKExpansionSupport, которые указывают Environment.getExternalStorageDirectory() в качестве корневого каталога.

Эти изменения должны помочь вам, по крайней мере, на полпути.

Также обратите внимание, что проблема с внешней SD-картой заключается в том, что их скорость сильно различается, обычно внутренняя SD-карта имеет как минимум приличную скорость. Возможно, вы можете разделить OBB на 2 файла (при условии, что вам не нужна функция файла обновления) и переместить один из них на внешний SD.

person Kai    schedule 09.03.2013
comment
Я не заинтересован во взломе системы, чтобы это заработало. Мой реальный вопрос заключался в том, не пропустил ли я где-то какую-то настройку, чтобы все это происходило на внешней SD-карте. Судя по комментариям CommonWare выше, это не так. - person btschumy; 09.03.2013
comment
Нет. Но это не взлом системы, потому что он никогда не был частью платформы Android. Это просто набор библиотек и сервисов, предоставляемых приложением Google Play. - person Kai; 09.03.2013

У меня была такая же проблема, но я нашел способ ее решить.

Если вы удалите OBB, то DoesFileExist (в Helpers.java) вернет false. В вашей ExpDownloaderActivity, где вы проверяете extensionFilesDelivered, вы будете использовать эту информацию для повторной загрузки OBB.

Я изменил doFileExist и extensionFilesDelivered таким образом, чтобы возвращалось не логическое значение, а целое число со следующим значением:

fileStatus == 0: OBB отсутствует (необходимо скачать)

fileStatus == 1: OBB доступен и может быть распакован в другое место

fileStatus == 2: данные в OBB уже хранятся в другом месте

Теперь трюк: после распаковки данных из OBB в мое любимое место, я заменяю оригинальный OBB файлом с таким же именем, который содержит только размер исходного OBB в виде строки. Это освобождает занятое место на SD-карте.

Дальнейшие вызовы doFileExist и extensionFilesDelivered вернут filestatus = 2, что означает, что никаких действий не требуется.

Вот мои изменения в Helpers.java:

static public int doesFileExist(Context c, String fileName, long fileSize,
        boolean deleteFileOnMismatch) {
    // the file may have been delivered by Market --- let's make sure
    // it's the size we expect


    File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
    if (fileForNewFile.exists()) {
        if (fileForNewFile.length() == fileSize) {
            return 1;
        } else if (fileForNewFile.length() < 100) {
            // Read the file and look for the file size inside
            String content = "";
            long isSize = 0;
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(fileForNewFile);
                char current;
                while (fis.available() > 0) {
                    current = (char) fis.read();
                    content = content + String.valueOf(current);
                }

            } catch (Exception e) {
                Log.d("ReadOBB", e.toString());
            } finally {
                if (fis != null)
                    try {
                        fis.close();
                    } catch (IOException ignored) {
                }
            }
            try {
                isSize = Long.parseLong(content);
            } catch(NumberFormatException nfe) {
                Log.d("ReadOBBtoInt", nfe.toString());
            } 
            if (isSize == fileSize) {
                return 2;
            }
        }
        if (deleteFileOnMismatch) {
            // delete the file --- we won't be able to resume
            // because we cannot confirm the integrity of the file
            fileForNewFile.delete();
        }
    }
    return 0;
}

Если размер файла OBB не совпадает, я читаю OBB и сравниваю с размером файла, хранящимся внутри (именно таким должен быть размер файла). Если это даст совпадение, я знаю, что файл уже обработан.

Это мои изменения в моей ExpDownloaderActivity:

int expansionFilesDelivered() {
    int fileStatus = 0;

    for (XAPKFile xf : xAPKS) {
        if (xf.mFileSize > 0) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
            fileStatus = Helpers.doesFileExist(this, fileName, xf.mFileSize, false);
            if (fileStatus==0)
                return 0;
        }
    }
    return fileStatus;
}

В onCreate ExpDownloaderActivity:

    initializeDownloadUI();

    int fileStatus = expansionFilesDelivered();
    if (fileStatus==0) {        // OBB is missing
            // ... Download the OBB file, same as on Downloader example
    } else if (fileStatus==1) {
        validateXAPKZipFiles(); // and, if OBB has no errors, unpack it to my favorite place
                                // if done, create a new OBB file with the original name
                                // and store a string with the original filesize in it.
    } else {
        finish();               // No action required        }

Таким образом, я распаковываю данные, куда захочу, и, как упоминалось в ОП, мне не нужно место на SD-карте для полного OBB и распакованных данных.

person thpitsch    schedule 04.08.2013