В процессе эксплуатации мобильных решений на платформе 1С часто возникает задача реализовать механизм обновления приложения в обход официальных магазинов (таких как Google Play). Это актуально для корпоративных приложений, которые не предназначены для публичного доступа, или в случаях, когда необходимо оперативно доставить критическое обновление пользователям. Рассмотрим, как программно реализовать этот процесс, с какими трудностями можно столкнуться на разных версиях Android и как их преодолеть.
Типичный алгоритм обновления состоит из трех этапов: получение двоичных данных нового релиза с сервера, сохранение их в файл формата .apk на мобильном устройстве и запуск процесса инсталляции. Разберем пример кода, который часто используется для этой цели.
На стороне сервера создается функция, которая формирует ответ с файлом:
&НаСервере
Функция ЗагрузитьФайлНаСервере()
лСтруктураЗапроса = Новый Структура;
лСтруктураЗапроса.Вставить("method", "get_releases");
лСтруктураЗапроса.Вставить("nomer", НовыйРелиз);
лОтвет = ОбщийМодуль2.ВыполнитьЗапросКБазе(лСтруктураЗапроса);
Если лОтвет.Свойство("success") И лОтвет.success = "true" Тогда
лКаталог = КаталогВременныхФайлов();
лИмя = "app.shk-arm.apk";
Попытка
УдалитьФайлы(лКаталог + лИмя);
Исключение
// Файл может отсутствовать, это не критично
КонецПопытки;
Попытка
лДвоичныеДанные = ОбщийМодуль2.ЗначениеИзСтрокиXML(лОтвет.data_file);
лДвоичныеДанные.Записать(лКаталог + лИмя);
Возврат лКаталог + лИмя;
Исключение
Сообщить(ОписаниеОшибки());
Возврат "";
КонецПопытки;
КонецЕсли;
Возврат "";
КонецФункции
На стороне клиента вызывается процедура установки:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
лФайл = ЗагрузитьФайлНаСервере();
Если НЕ лФайл = "" Тогда
лОповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект, лФайл);
ПоказатьВопрос(лОповещение, "Установить полученный релиз?", РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияВопроса(Результат, Параметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Попытка
ЗапуститьПриложение(Параметры);
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию: на Android 7 данный код работает корректно, а на Android 5 файл сохраняется, но ЗапуститьПриложение не инициирует установку. Выясним причину такого поведения. Основная проблема заключается в том, где именно сохраняется APK-файл.
При использовании функции КаталогВременныхФайлов() приложение сохраняет данные во внутреннюю память, доступ к которой имеет только само приложение. Пути могут выглядеть как /data/data/com.e1c.mobile/cache/ или /data/user/0/.... Когда мы вызываем ЗапуститьПриложение, 1С передает команду операционной системе запустить "Установщик пакетов" (Package Installer). Однако этот установщик является отдельным системным процессом.
Важный момент: на старых версиях Android (включая Android 5) системный установщик не имеет прав на чтение файлов из приватных директорий других приложений. Именно поэтому, даже если файл физически записан, система "не видит" его при попытке запуска установки.
Чтобы системный установщик смог прочитать APK, файл необходимо сохранить в общедоступное место, например, в папку загрузок на внешней памяти (SD-карте или эмулируемом общем хранилище). Для этого проанализируем следующие шаги:
КаталогВременныхФайлов() следует программно определить путь к папке /sdcard/Download/ или использовать стандартные диалоги выбора файлов.ЗапуститьПриложение(ПутьКФайлуВЗагрузках) сработает корректно, так как у Package Installer будут права на чтение этого файла.В обсуждениях часто упоминается механизм работы через файлы .v8i. Разберем подробнее, что это дает. Если ваше мобильное приложение работает как "Мобильный клиент", то обновлять сам APK (оболочку) нужно крайне редко. Достаточно обновить основную базу на сервере, и клиент сам скачает изменения метаданных при следующем подключении.
Для "Автономного мобильного приложения" существует другой механизм — загрузка файла конфигурации .cf или расширения .cfe. Вместо того чтобы переустанавливать всё приложение целиком, мы можем:
update.cf (или update.cfe).УстановитьОбновлениеКонфигурации().Этот метод гораздо стабильнее, так как не требует взаимодействия с системным установщиком Android и не зависит от ограничений на установку из неизвестных источников.
При сборке мобильного приложения в "Сборщике мобильных приложений 1С" обратим внимание на следующие настройки:
Разрешение на установку: Начиная с Android 8.0, для программного запуска установки требуется разрешение REQUEST_INSTALL_PACKAGES. Хотя в Android 5 его еще не было, для универсальности решения его стоит включить в манифест при сборке.
Неизвестные источники: Программно обойти настройку телефона "Разрешить установку из неизвестных источников" невозможно. Приложение должно информировать пользователя о необходимости включить эту опцию в настройках безопасности Android перед запуском процесса обновления.
Проанализировав ситуацию, мы можем сделать вывод: для успешной инсталляции APK-файла на старых версиях Android (таких как Android 5), необходимо перенести файл из внутреннего кэша приложения в публичную директорию. Рассмотрим пример скорректированной логики:
1. Сохраняем файл по пути, доступному системе (например, используя префикс /sdcard/ или системные переменные окружения, доступные через средства ОС).
2. Проверяем расширение файла — оно строго должно быть .apk.
3. Вызываем ЗапуститьПриложение, передавая полный путь. Если платформа позволяет, желательно явно указать MIME-тип: application/vnd.android.package-archive.
Если же архитектура позволяет, самым надежным способом будет переход на обновление через .cf файлы, что полностью исключит проблемы с правами доступа к файловой системе Android при установке новых пакетов.