Объявления в манифесте
Средство обновления кэшированных файлов
Использование контракта средства обновления кэшированных файлов предназначено для синхронизации локальных копий файла с копиями на удалённых ресурсах, которыми управляет приложение-поставщик. Данный контракт специально предназначен для приложений, которые предоставляют доступ к хранилищу, куда пользователь регулярно сохраняет файлы, откуда он их открывает и обновляет их содержимое. Приложение SkyDrive в Windows – это хороший пример подобного взаимодействия. В других случаях, когда пользователь обычно пользуется средством выбора файлов для получения файлов и использования их каким-либо образом, но не возвращается к ним, использование контрактов средств выбора файлов полностью оправданно.
Если взглянуть на Главу 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript", мы увидим там некоторые вызовы методов, выполненные приложением, которое использует средство выбора файлов: Windows.Storage.CachedFileManager.deferUpdates и Windows.Storage.CachedFileManager.completeUpdatesAsync. Их использование показано в Сценариях 4 и 6 примера Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ). Проще говоря, это те вызовы, которые делает приложение-потребитель файлов, если и когда оно выполняет запись в файлы, которое оно получило от средства выбора файла. Оно делает это потому что не узнает (и ему не следует об этом беспокоиться), есть ли у поставщика файла другая копия файла в базе данных, на веб-сервисе, и так далее, с которой нужно синхронизировать данный файл. Если приложение-поставщик нуждается в обработке синхронизации, вызовы приложением-приемником этих методов будут вызывать необходимый интерфейс средства обновления кэшированных файлов приложения-поставщика, который может быть показан, а может и нет, в зависимости от конкретной ситуации. Даже если приложение-потребитель не вызывает эти методы, приложение-поставщик будет получать оповещения об изменениях, но не сможет показать какой-либо пользовательский интерфейс.
Этот контракт работает с двумя направлениями, в зависимости от того, нужно ли ему обновить локальную (кэшированную) копию файла, или удалённую (исходную) копию. В первом случае, поставщик запрашивает обновление локальной копии, обычно когда приложение-потребитель пытается получить доступ к файлу (берет его из списков FutureAccessList или MostRecentlyUsed объекта Windows.Storage.AccessCache (http://msdn.microsoft.com/library/windows/apps/br230566.aspx ); оно не запрашивает обновление напрямую). Во втором случае приложение-потребитель модифицирует файл, поэтому поставщику нужно передать изменения в исходную копию.
С точки зрения приложения-поставщика, необходимость в обновлениях возникает, когда оно предоставляет файл другому приложению. Это может произойти с помощью контрактов средств выбора файлов , как мы видели в предыдущем разделе, но это может случиться и через сопоставление типов файлов, а так же посредством контракта общего доступа. В последнем случае приложение-источник данных для общего доступа, в известном смысле, является поставщиком файла и может так же использовать контракт средства обновления кэшированных файлов. Коротко говоря, если вам нужно, чтобы приложение-поставщик файлов смогло отслеживать и синхронизировать обновления между локальной и удалённой копиями файла, ему нужно использовать этот контракт.
Поддержка контракта начинается с объявления в манифесте, как показано ниже, где Начальная страница (Start page) указывает на страницу, реализующую интерфейс средства обновления кэшированных файлов. Эта страница обрабатывает необходимые для обновления файлов события и может быть показана, а может и не быть показана пользователю, как мы увидим позже.
Следующий шаг поставщика заключается в том, чтобы показать, когда данный StorageFile следует передать этому контракту. Он делает это, вызывая Windows.Storage.Provider.CachedFileUpdater.setUpdateInformation ( http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155) для предоставленного файла, как показано в Сценарии 3 примера "Контракт Средство выбора файла" (http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155 ), который я снова, упоминать как пример поставщика (js/fileOpenPickerScenario3.js):
function onAddFile() { // Ответ на нажатие кнопки Add (Добавить) Windows.Storage.ApplicationData.current.localFolder.createFileAsync("CachedFile.txt", Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) { Windows.Storage.FileIO.writeTextAsync(file, "Cached file created...").then( function () { Windows.Storage.Provider.CachedFileUpdater.setUpdateInformation( file, "CachedFile", Windows.Storage.Provider.ReadActivationMode.beforeAccess, Windows.Storage.Provider.WriteActivationMode.notNeeded, Windows.Storage.Provider.CachedFileOptions.requireUpdateOnAccess); addFileToBasket(localFileId, file); }, onError); }, onError); };
Примечание. setUpdateInformation находится в пространстве имен Windows.Storage.Provider и отличается от объекта Windows.Storage.CachedFileManager, который используется с другой стороны контракта. Будьте внимательны и не перепутайте их.
Метод setUpdateInformation принимает следующие аргументы:
- StorageFile для файла.
- Строку идентификации содержимого, которая идентифицирует удалённый ресурс, с которым нужно выполнять синхронизацию.
- ReadActivationMode (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.readactivationmode.asp), который показывает, может ли вызывающее приложение читать локальные файлы, не обновляя их. Может принимать значения notNeeded и beforeAccess.
- WriteActivationMode (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.writeactivationmode.aspx ) показывает, может ли вызывающее приложение осуществлять запись в окальный файл, и вызывает ли запись процесс обновления. Принимает значения notNeeded, readOnly, и afterWrite.
- Одно или большее количество значений из CachedFileOptions (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.cachedfileoptions.aspx ) (они могут быть скомбинированы с помощью побитовой операции OR), которые описывают способы, которыми можно получить доступ к локальному файлу без вызова обновления. Возможные значения - none (нет обновления), requireUpdateAccess (обновление при доступе к локальному файлу), useCachedFileWhenOffline (будет осуществлено обновление при доступе, если это запросит вызывающее приложение, доступ разрешается, если нет сетевого соединения), и denyAccessWhenOnline (запускает обновление при попытке доступа и требует сетевое соединение).
С помощью этого вызова, другими словами, поставщик управляет тем, как и когда ему следует активироваться для обработки обновления, когда осуществляется доступ к локальному файлу.
Итак, всего у нас есть два случая, когда приложение-поставщик может быть активировано и у него может быть запрошен показ пользовательского интерфейса: первый случай – когда вызывающее приложение обновляет файл, и второй – когда вызывающее приложение пытается получить доступ к файлу, но нуждается в обновлении перед чтением его содержимого.
Прежде чем переходить к техническим подробностям, давайте посмотрим, как всё это выглядит для пользователя. Для того, чтобы увидеть средство обновления кэшированных файлов в действии с использованием примера, активируйте его с использованием средства выбора файлов из другого приложения. Для начала, однако, запустите пример поставщика для того, чтобы удостовериться в том, что его контракты зарегистрированы в системе. Затем запустите вышеупомянутый пример "Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ). В нём, Сценарии 4, 5 и 6 вызывают взаимодействие с контрактом обновления кэшированных файлов. Сценарии 4 и 6 осуществляют запись в файл для вызова обновления удалённой копии. Сценарий 5 получает доступ к локальному файлу, что вызывает обновление локального файла как часть этого процесса.
Обновление локального файла: пользовательский интерфейс
В Сценарии 5 (обновление локального файла), начните с прикосновения к кнопке Выбрать локальный файл (Pick Cached File) в пользовательском интерфейсе, показанном здесь:
Это запустит пример поставщика. На данном экране, выберите Сценарий 3, в итоге, вы увидите интерфейс, показанный на рис. 3.4. Это режим примера поставщика, который отображает средство выбора файлов поставщика (js/fileOpenPickerScenario3.js), где он вызывает setUpdateInformation. Это еще не интерфейс для средства обновления кэшированных файлов. Щелкните кнопку Add File to Basket (Добавить файл в корзину), и коснитесь кнопки Open (Открыть). Это вернет вас в первое приложение (к примеру, средства выбора файлов на вышеприведенном рисунке), где теперь будет активна кнопка Output Latest Version (Вывести самую свежую версию). Прикосновение к этой кнопке теперь активирует например поставщика с помощью контракта средства обновления кэшированных файлов, как показано на рис. 3.5. Это происходит, когда нужно обновить локальную копию кэшированного файла.
Рис. 3.4. Интерфейс примера поставщика для выбора файла. Для предоставленного файла вызван метод setUpdateInformation, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
Рис. 3.5. Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для локального файла
Обратите внимание на описания в примере. В то время, как пример показывает интерфейс по умолчанию, интерфейс средства обновления кэшированных файлов не отображается до тех пор, пока не нужно будет разрешить конфликт или запросить идентификационные данные. Часто в подобном взаимодействии нет необходимости и поставщик незаметно предоставляет обновление локального файла или показывает, что файл находится в актуальном состоянии. Интерфейс примера просто предоставляет обе эти возможности для явного выбора (и не забудьте выбрать одну из них, так как выбор Cancel (Отмена) приведет к выдаче исключения).
Обновление удалённого файла: пользовательский интерфейс
В Сценарии 6 (обновление удалённого файла) примера средства выбора файла, мы можетм видеть взаимодействие, которое присутствует, когда приложение-потребитель вносит изменения в свою локальную копию, таким образом, вызывая обновление удалённой копии. Начнём с прикосновения к кнопке Get Save File (Выполнить сохранение файла) в окне, показанном ниже:
В средстве выбора файлов, выберите пример поставщика как источник, что приведет к запуску интерфейса, показанного на рис. 3.6. посредством контракта средства сохранения файла, реализованного в html/fileSavePickerScenario3.html и js/fileSavePickerScenaro3.js. Если вы посмотрите в файл JavaScript, вы увидите вызов setUpdateInformatio, который осуществляется, когда вы вводите имя файла и нажимаете Сохранить (Save). Выполнение этого действия возвращает вас в пример средства работы с файлами выше и теперь должна быть доступна кнопка Запись в файл (Write To File). Прикосновение к этой кнопке повторно запускает пример поставщика посредством контракта обновления кэшированных файлов с пользовательским интерфейсом, показанным на рис. 3.7. Этот интерфейс предназначен для демонстрации того, как приложение-поставщик может обработать перезапись или переименование удалённого файла.
Рис. 3.6. Интерфейс примера поставщика для сохранения файла. Метод setUpdateInformation снова вызывается для предоставленного файла, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
Рис. 3.7. Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для удалённого файла
События обновления
Давайте посмотрим, как контракт обновления кэшированных файлов выглядит в коде. Как вы можете ожидать, приложение-поставщик запускается, загружается начальная страница (cachedFileUpdater.html в корневом разделе проекта), обработчик activated вызывается с видом активации cachedFileUpdater. Это происходит и при обновлении удалённого файла, и локального, и, как мы здесь видим, код активации один и тот же в обои случаях. Здесь eventObject.detail – это объект WebUICachedFileUpdaterActivatedEventArgs (http://msdn.microsoft.com/library/windows/apps/hh701752.aspx ), который содержит свойство cachedFileUpdaterUI (типа CachedFileUpdaterUI (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.cachedfileupdaterui.aspx )), вместе с обычным набором из kind, previousExecutionState, и splashScreen. Вот как это выглядит в файле js/cachedFileUpdater.js примера поставщика:
function activated(eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.cachedFileUpdater) { cachedFileUpdaterUI = eventObject.detail.cachedFileUpdaterUI; cachedFileUpdaterUI.addEventListener("fileupdaterequested", onFileUpdateRequest); cachedFileUpdaterUI.addEventListener("uirequested", onUIRequested); switch (cachedFileUpdaterUI.updateTarget) { case Windows.Storage.Provider.CachedFileTarget.local: // Код опущен: настраивает пример для показа cachedFileUpdaterScenario1 // при необходимости. break; case Windows.Storage.Provider.CachedFileTarget.remote: // Код опущен: настраивает пример для показа cachedFileUpdaterScenario2 // при необходимости. break; } } }
Когда приложение-поставщик запускается для обновления локального файла из удалённого источника, свойство cachedFileUpdaterUI.updateTarget будет иметь значение local, как вы можете видеть выше. Кода у приложения запрашивается обновление удаленного файла с использованием локальных изменений, цель будет установлена в значение remote. Всё, что пример выполняет в этих случаях – это указывает либо на html/cachedFileUpdaterScenario1.html (рис. 3.5.) либо на html/cachedFile-UpdaterScenario2.html (рис. 3.7) как на пользовательский интерфейс для обновления.
Интерфейс, на самом деле, не показывается сразу. В первую очередь объект CachedFileUpdaterUI вызывает своё событие fileUpdateRequested (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.cachedfileupdaterui.fileupdaterequested.aspx ) для выполнения попытки незаметного обновления. Здесь eventArgs является объектом FileUpdateRequestedEventArgs с единственным свойством request (FileUpdateRequest (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.fileupdaterequest.aspx )), объектом, который сохраняют в переменной, которая доступна из интерфейса обновления.
Если есть возможность незаметно обновить локальный файл, выполните следующее:
- Так как вы, весьма вероятно, будете пользоваться асинхронными операциями для выполнения обновления, получите отложенную операцию из request.getDeferral.
-
Для обновления содержимого локального файла, используйте эти опции:
- Если у вас уже есть StorageFile с новым содержимым, просто вызовите request.updateLocalFile (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.fileupdaterequest.updatelocalfile.aspx ). Это асинхронный вызов, при его использовании вам не нужно получать отложенную операцию.
- Объект StorageFile локального файла будет в request.file. Вы можете открыть этот файл и записать в него всё, что нужно. Это обычно начинает асинхронную операцию, после которой вы вернетесь в обработчик события.
- Для обновления содержимого удаленного файла, скопируйте содержимое из request.file в удалённый источник.
- В зависимости от результат обновления, установите значение request.status в значение из FileUpdateStatus (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.fileupdatestatus.aspx ): complete (копии синхронизированы), incomplete (обновление не было полностью завершено, но локальная копия всё еще доступна), userInputNeeded (обновление не удалось, так как требуется ввод учетных данных или разрешение конфликта), currentlyUnavailable (удаленная версия файла недоступна, и локальная версия недоступна), failed (синхронизация невозможна ни сейчас, ни в будущем, так как удалённая версия файла удалена), и completeAndRenamed (исходная версия файла была переименована, обычно – для разрешения конфликтов).
- Если вы запросили отложенную операцию и обрабатываете результат в обработчиках завершения и ошибки, вызовите метод отложенного объекта complete для того, чтобы завершить обновление.
В итоге, теперь поставщик может знать заранее, что он не может выполнить незаметное обновление вовсе – пользователь может быть не авторизованным на удалённом сервисе (или учетные данные нужны каждый раз), может быть конфликт, требующий разрешения и так далее. В подобных случаях обработчик события должен проверить значение cachedFileUpdaterUI.uiStatus (типа UIStatus (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.uistatus.aspx )) и соответствующим образом установить свойство request.status:
- Если состояние пользовательского интерфейса имеет значение visible, переключиться к этому интерфейсу и вернуться из обработчика события. Отложенная операция завершится, когда пользователь введет нужные данные посредством пользовательского интерфейса.
- Если состояние пользовательского интерфейса имеет значение hidden, установить request.status в значение userInputNeeded и вернуться. Это вызовет событие CachedFileUpdaterUI.onuiRequested , за которым последует другое событие fileUpdateRequested, где uiStatus будет иметь значение visible, в таком случае вы переключитесь на ваш пользовательский интерфейс.
- Если состояние пользовательского интерфейса имеет значение unavailable, установить request.status в значение currentlyUnavailable.
Вы можете найти кое-что из этого в обработчике onFileUpdateRequest примера. Он, на самом деле, обрабатывает лишь проверку uiStatus, так как не пытается выполнить незаметное обновление (как описано ниже, в комментариях):
function onFileUpdateRequest(e) { fileUpdateRequest = e.request; fileUpdateRequestDeferral = fileUpdateRequest.getDeferral(); // Попытка незаметного обновления с использованием fileUpdateRequest.file , или вызов // fileUpdateRequest.updateLocalFile в локальном случае, setting fileUpdateRequest.status // соответственно, затем вызов fileUpdateRequestDeferral.complete(). В противном случае, если вы // знаете, что понадобится действие пользователя, исполнение следующего кода. switch (cachedFileUpdaterUI.uiStatus) { case Windows.Storage.Provider.UIStatus.hidden: fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.userInputNeeded; fileUpdateRequestDeferral.complete(); break; case Windows.Storage.Provider.UIStatus.visible: // Переключение на интерфейс обновления (настроено в событии activated) var url = scenarios[0].url; WinJS.Navigation.navigate(url, cachedFileUpdaterUI); break; case Windows.Storage.Provider.UIStatus.unavailable: fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.failed; fileUpdateRequestDeferral.complete(); break; } }
Опять же, если произойдет незаметное обновление, пользовательский интерфейс приложения-поставщика никогда не будет показан пользователю. В случае с примером поставщика, так как незаметное обновление не происходит никогда, он всегда проверяет uiStatus. Когда приложение запущено для обслуживания контракта, мы закончим скрытым вариантом и вернем userInputNeeded, как произошло бы, если бы вы попытались выполнить незаметное обновление, но возвращен был бы тот же статус. В любом случае, объект CachedFileUpdateUI вызовет своё событие uiRequested (http://msdn.microsoft.com/library/windows/apps/windows.storage.provider.cachedfileupdaterui.uirequested.aspx ), сообщая приложению-поставщику, что система делает пользовательский интерфейс видимым. Приложение, на самом деле, может отложить инициализацию своего пользовательского интерфейса до возникновения этого события, так как он не выполняет никаких функций при незаметном обновлении.
После этого событие fileUpdateRequested снова будет вызвано с uiStatus, теперь установленным в значение visible. Отметим, как вышеприведенный код будет вызывать в этом случае request.getDeferral но не будет вызывать его метод complete.
Мы откладывает этот шаг до того момента, когда работа с пользовательским интерфейсом приведет к нужному результату (и, на самом деле, мы оставляем и запрос и отложенный вызов для использования из кода пользовательского интерфейса).
Пользовательский интерфейс обновления данных ответственен за приём любых данных от пользователя, необходимых для выполнения задачи: пользователь может вводить учетные данные, указывать, какую из копий файлов следует сохранить (локальную или удаленную), разрешать переименование конфликтующего файла (при обновлении удалённого файла) и так далее. При обновлении локального файла, осуществляется запись в StorageFile в request.file или вызывается request.updateLocalFile; в случае с обновлением удаленного файла, данные из локальной копии копируются в request.file.
Для того, чтобы завершить обновление, код интерфейса устанавливает request.status в значение complete (или, если произошёл сбой, в другое подходящее значение) и вызывает метод complete отложенной операции. Это изменит статус кнопок, предоставленных системой, расположенных у нижней части экрана, как вы можете видеть на рис. 3.5. и рис. 3.7., а именно, активирует кнопку OK и деактивирует Cancel (Отмена). В примере поставщика обе кнопки просто выполняют эту пару строк кода для этой цели:
fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.complete; fileUpdateRequestDeferral.complete();
В целом, взаимодействие между системой и приложением при реализации контракта средства обновления кэшированных файлов выглядит довольно простым и понятным: обработка событий, копирование данных туда, куда нужно и обновление статуса запроса. Настоящая работа по реализации этого контракта заключается в том, чтобы сначала решить, когда вызывать setUpdateInformation и затем предоставить пользовательский интерфейс для поддержки обновления локальных или удаленных файлов при определенных обстоятельствах. Это, конечно, включит в себя интенсивное взаимодействие с вашей серверной системой хранения.