Разработчики мобильных приложений на платформе 1С часто сталкиваются с неприятной особенностью: мобильное приложение успешно генерирует файл (например, выгрузку данных в формате XML), во встроенном диспетчере файлов Android этот файл прекрасно виден и открывается, но стоит подключить устройство к компьютеру через USB-кабель — и целевая папка на ПК оказывается пустой. При этом, если вручную скопировать файл внутри мобильного устройства через любой файловый менеджер, копия мгновенно отображается на компьютере. Создается ложное ощущение, что у созданного файла некорректно настроены права доступа.
В этой статье мы подробно разберем природу этого поведения, которая кроется в особенностях работы операционной системы Android, и рассмотрим практические способы решения этой проблемы для разработчиков 1С.
Для начала проанализируем, как современный компьютер взаимодействует с памятью смартфона. В старых версиях Android (до версии 4.0) при подключении к ПК память устройства монтировалась как обычная флешка (режим Mass Storage). Компьютер получал прямой низкоуровневый доступ к файловой системе. Минус этого подхода заключался в том, что в момент подключения память была заблокирована для самого телефона.
В современных устройствах используется протокол MTP (Media Transfer Protocol). Компьютер больше не работает с файловой системой напрямую. Вместо этого он отправляет запросы операционной системе Android, которая обращается к специальной базе данных индексатора — MediaStorage. ПК «видит» только то, что зарегистрировано в этой базе данных.
Когда мы программно создаем файл из мобильной платформы 1С, происходят следующие процессы:
MediaScanner) еще не знает о существовании нового файла. Она работает по расписанию, при перезагрузке устройства или при определенных системных триггерах.MediaStore записи о файле нет, протокол MTP сообщает компьютеру, что папка пуста.Таким образом, наша задача как разработчиков — заставить операционную систему Android принудительно проиндексировать только что созданный файл, чтобы он немедленно стал доступен по USB.
Автор темы на форуме опытным путем обнаружил интересную закономерность: если заменять (перезаписывать) уже существующий файл, с которым ранее все было в порядке, то изменения сразу видны на ПК.
Выясним, почему это работает. Если файл с определенным именем уже был однажды проиндексирован системой MediaScanner, запись о нем присутствует в базе данных MediaStore. При перезаписи этого файла (даже если у него меняется размер и содержимое) операционная система не создает новую запись, а лишь обновляет существующую. Соответственно, компьютер через протокол MTP мгновенно видит обновленный файл.
Как реализовать этот метод в коде 1С:
Мы можем один раз вручную или программно создать «файл-пустышку» в целевой директории, дождаться его индексации (например, перезагрузив устройство), а затем в регулярных процессах обмена просто перезаписывать его содержимое.
// Пример перезаписи существующего XML-файла в мобильной 1С
ПутьКФайлу = ОбщегоНазначенияКлиентСервер.ПолучитьИмяФайлаВоВременномКаталоге("export.xml");
НовыйФайл = Новый Файл(ПутьКФайлу);
// Если файл уже создан ранее, мы просто открываем его на запись без изменения пути
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ЗаписьXML.ОткрытьФайл(ПутьКФайлу, ПараметрыЗаписи);
ЗаписьXML.ЗаписатьОбъявление();
ЗаписьXML.ЗаписатьНачалоЭлемента("ДанныеМобильногоПриложения");
// ... запись основного тела документа ...
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();
Этот метод хорош своей простотой и отсутствием необходимости писать дополнительный низкоуровневый код. Однако он не подходит для систем, где имя файла должно быть динамическим (например, содержать дату и время выгрузки или уникальный GUID).
Для полноценного и надежного решения проблемы необходимо использовать возможности операционной системы Android по «проталкиванию» файлов в медиа-базу. Самым правильным способом является вызов метода MediaScannerConnection.scanFile().
Поскольку в мобильной платформе 1С нет прямого доступа к Java-классам Android, мы можем наладить эту связь с помощью создания Внешней Компоненты (ВК) для мобильной платформы. Компонента пишется на языке Java (или C++) и компилируется в виде библиотеки.
Рассмотрим по шагам, какой Java-код должен выполняться внутри компоненты для принудительного сканирования созданного файла:
// Java-код для интеграции во внешнюю компоненту
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.content.Context;
public void scanNewFile(Context context, String filePath) {
MediaScannerConnection.scanFile(
context,
new String[] { filePath },
null,
new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
// Сканирование успешно завершено, файл внесен в базу MediaStore
// Теперь компьютер гарантированно увидит его по USB
}
}
);
}
После интеграции такой компоненты в мобильное приложение 1С, сразу после закрытия файла (например, метода Записать() или Закрыть() у соответствующих объектов 1С), достаточно вызвать метод компоненты, передав туда полный путь к файлу на устройстве. Система Android мгновенно проиндексирует файл.
Если разработка собственной внешней компоненты кажется вам слишком трудоемкой, можно воспользоваться альтернативным вариантом, предложенным в обсуждении форума. Мы можем создать простейшее Android-приложение (в формате APK), которое будет выполнять единственную задачу — принимать путь к файлу и отправлять системный Intent сканирования, а затем вызывать его из мобильной 1С.
Для вызова внешнего приложения из мобильной платформы используется метод ЗапуститьПриложение(). Разберем пример реализации вызова:
// Код в мобильном приложении 1С
// Предполагается, что на устройстве установлено наше вспомогательное APK-приложение
ИмяФайлаДляСканирования = "/storage/emulated/0/Download/export_data.xml";
ПараметрыЗапуска = Новый Массив;
ПараметрыЗапуска.Добавить("-d");
ПараметрыЗапуска.Добавить("file://" + ИмяФайлаДляСканирования);
// Запуск сторонней утилиты, умеющей инициировать сканирование медиа-файлов
ЗапуститьПриложение("android.intent.action.MEDIA_SCANNER_SCAN_FILE", ПараметрыЗапуска);
Важное примечание: Начиная с Android 10 (API level 29) и выше, в операционной системе была введена концепция Scoped Storage (изолированное хранилище). Прямой вызов общего сканера через Intent ACTION_MEDIA_SCANNER_SCAN_FILE для произвольных папок может быть ограничен политиками безопасности. Поэтому настоятельно рекомендуется сохранять файлы в стандартные публичные директории.
При разработке мобильного приложения важно правильно выбирать пути для сохранения файлов. Если вы сохраняете файлы во внутренний кэш приложения или в глубоко вложенные произвольные папки в корне памяти устройства, служба MediaScanner будет игнорировать их с высокой долей вероятности.
Для файлов, предназначенных для передачи на ПК, старайтесь использовать системные публичные папки:
/Documents (Документы)/Download (Загрузки)Операционная система Android настроена таким образом, что сканирование этих папок происходит значительно чаще, а ограничения безопасности Scoped Storage для них более гибкие. Получить пути к этим папкам в мобильной 1С можно с помощью системных функций работы с файловой системой или стандартных путей окружения Android.
Если вам нужно срочно скопировать файлы на компьютер во время тестирования разработки, а код еще не доработан, можно воспользоваться одним из административных методов «сброса» кэша индексатора:
После перезапуска Android запустит полное сканирование всей файловой системы устройства заново, база MediaStore обновится, и все ранее созданные файлы из мобильной 1С отобразятся на вашем компьютере.