[Обязательно к прочтению] Принцип контейнера APP, который должен понимать внешний интерфейс

внешний интерфейс внешний фреймворк

Контейнер приложения, короче говоря, приложение размещает операционную среду приложения определенного типа (H5/RN/Weex/Applet/Flutter...) и может активно вмешиваться и расширять функции для достижения цели расширения возможностей, оптимизации производительности, и улучшение опыта, например предварительная выборка данных страницы (предварительная выборка), чтобы сократить доступное время страницы, WebAR, предоставляющий возможности AR для взаимодействия H5, Native map и H5 составного рендеринга.

предварительная выборка данных Возможности веб-AR Родной и композитный H5

В этой статье в основном описывается конструкция контейнера H5 (WebView).

Давайте сначала проведем аналогию, кратко взглянем на Android и iOS с точки зрения H5, а затем облегчим понимание механизма построения контейнера WebView.

содержание H5 Android iOS
окно Window PhoneWindow UIWindow
управление страницей Html Activity UIViewController
Область содержимого body contentView(DecorView) view
Обычный интерфейс (контейнер, текст, изображение) div, контент, изображение Представление, текстовое представление, изображение UIView, UILabel, UIImageView

Из приведенного выше сравнения мы видим, что Native и H5 имеют много общего, например: H5 — это html для создания страницы, Android — это действие для создания страницы, iOS — это UIViewController для создания страницы. Разница в том, Сам Native имеет совершенный механизм управления стеком страниц, который контролирует переход между страницами в одной и той же среде выполнения; он также может управлять несколькими окнами (Window) и несколькими потоками/процессами (только для Android), чтобы способствовать рациональному использованию ресурсов для обеспечения производительность основного потока/процесса - это опыт приложения. Однако сам H5 ограничен операционной средой и может быть активен только в одном окне. В настоящее время ему не хватает зрелого механизма управления стеком страниц в той же среде выполнения. Текущий режим SPA переключает представления для имитации «переходов страниц», что улучшенное взаимодействие с WebApp. Достигнуто хорошо. Поэтому в приложении H5 рассчитывает использовать больше нативных возможностей.

В приложении доступ к странице h5 осуществляется через WebView. Давайте посмотрим на официальное определение WebView:

A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.

Фактически, реализация WebView на обоих концах Android/iOS наследуется от его базового класса View/UIView. Для Native Native WebView сам генерирует представление, загружая страницу h5, анализирует и синтезирует пользовательский интерфейс через ядро ​​Chromium/WebKit, а экземпляр View of Activity/UIViewController добавляет веб-представление в уровень представления через addView(Android)/addSubview(iOS). Пользовательский интерфейс синтезируется, а затем отображается на экране.

Мы знаем, что приложения могут использовать системные возможности, но WebView (аналог браузеров) не предоставляется по умолчанию из соображений безопасности. Одна из основных целей создания контейнеров — открыть доступные возможности приложений для контейнеров. Поговорим о том, как обеспечивается эта часть возможности, то есть о строительстве каналов моста, которые будут рассмотрены ниже.


канал моста

Мост имеет значение подключения.Он соединяет два не связанных между собой моста, который визуально описывает «возможности приложения», которые доставляются в контейнер в виде «каналов».По сути, это как включить наш H5, чтобы использовать эти возможности. Давайте посмотрим на идеи реализации обоих концов по отдельности:

Android

Связь сопоставления объектов Java и JS устанавливается с помощью метода addJavascriptInterface WebView. Для системной версии требуется 4.2+, основные приложения в основном адаптированы на основе 5+, а решение, совместимое с более ранней версией, пропускается. следующее:

  1. Определите класс реализации Android JavascriptInterface и вызовите метод, который сопоставляется с контекстом JS WebView (то есть с объектом монтирования окна).

    package ...
    ...
    import android.webkit.JavascriptInterface;
    public class JSBridgeChannel {
        @JavascriptInterface
        // JS侧JSON.stringify序列化处理options对象
        public void call(String api, String options){
            //根据api及传递过来的参数处理Native对应的功能
            ...
        }
    }
    
  2. Во время loadUrl WebView (чтобы убедиться, что канал моста готов до запуска H5 JS) установите отношение сопоставления между объектами экземпляра класса Android и объектами JS с помощью метода addJavascriptInterface WebView.

    @Override
    public void loadUrl(String url) {
        ...
        //…构造函数所需参数;AliJSBridge 为JS映射对象名
        mWebView.addJavascriptInterface(new JSBridgeChannel(…), "JSBridge");
    }
    
  3. JS в WebView можно вызывать синхронно

    window.JSBridge.call("api", JSON.stringify(options));
    

Вы также можете использовать JS, предоставленный системой Android, для печати журнала API onConsoleMessage в отладочном собственном коде и получения сообщения для данных связи, но это не рекомендуется.

iOS

WKWebView предоставляет метод MessageHandler для обработки взаимодействия данных между JS и Native. Его преимуществами являются синхронные вызовы, лучшая производительность/стабильность и меньшее использование памяти. Для системной версии требуется iOS 8+, а основные приложения в основном адаптированы под 9+.

Когда WKWebView инициализируется:[[WKWebView alloc] initWithFrame:frame configuration:config], параметры его конфигурацииconfigurationдаWKWebViewConfigurationПараметр типа, WKWebViewConfiguration имеет свойство userContentController, котороеWKUserContentControllerПараметр типа, WKUserContentController имеет метод экземпляра[addScriptMessageHandler:name:](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537172-addscriptmessagehandler?language=objc), в официальном определении четко указано, что он может установить канал связи между JS и Native:

Adding a script message handler with name name causes the JavaScript function window.webkit.messageHandlers.name.postMessage(messageBody) to be defined in all frames in all web views that use the user content controller.

  1. Инициализируйте WKWebView во время инициализации VC, создайте объект WKWebViewConfiguration и настройте объект MessageHandler. пройти черезaddScriptMessageHandler:name:Добавьте объект, реализующий протокол WKScriptMessageHandler, и имя метода, который будет вызываться JS. Примечание. Не забудьте передатьremoveScriptMessageHandlerForNameУдалить релиз.

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    //添加ScriptMessageHandler,即H5侧可以调用的方法bridge
    [configuration.userContentController addScriptMessageHandler:self initWithDelegate:self] name:@"bridge"];
    //创建WKWebView
    [[WKWebView alloc] initWithFrame:frame configuration:configuration];
    
  2. выполнитьWKScriptMessageHandlerМетод прокси-сервера протокола, этот метод будет вызываться, когда JS отправляет собственные сообщения через window.webkit.messageHandlers.

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
        if ([@"bridge" isEqualToString:message.name]){
            //提取api
            NSString *api = [NSString stringWithString:message.body[@"_api_"]];
            //根据api及传递过来的参数去调用Native对应的功能
            ...
        }
    }
    
  3. JS в WebView можно вызывать синхронно

    const options = {
        //注意这里,是一个技巧性的处理方式,可以避免过多的messageHandlers注册
       _api_: ‘apiName',
       ...
    };
    window.webkit.messageHandlers['bridge'].postMessage(options);
    

Метод вызова Unified Bridge API

Вышеупомянутое состоит в том, что два конца синхронно присоединяют методы объекта, которые JS может вызывать, к JContext WebView, то есть Window, но использование двух концов все еще отличается. Чтобы облегчить унифицированное использование H5, обе стороны также могут внедрить совместимый инкапсулированный сценарий JS, чтобы объединить функции моста на обеих сторонах в одну.JSBridge.call(api, options, success, failure)вызов формы.

  • Внедрение JS-скрипта на стороне Android (внедрение после mWebView.addJavascriptInterface):

    String js = "!(_ => {   // JS封装code })();";
    mWebView.evaluateJavascript(js, new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            ...
        }
    });
    
  • Внедрение JS-скрипта на стороне iOS:

    NSString *js = @"!(_ => {   // JS封装code })();";
    WKUserScript *script = [[WKUserScript alloc] initWithSource: js
                                                  injectionTime:WKUserScriptInjectionTimeAtDocumentStart  // 注入时机按需配置即可:WKUserScriptInjectionTimeAtDocumentStart->刚开始创建 Dom 时;WKUserScriptInjectionTimeAtDocumentEnd -> DomReady时
                                               forMainFrameOnly:NO];
    [_webView.configuration.userContentController addUserScript: script];
    

Если вас интересует реализация более низкого уровня, вы можете продолжить детализацию.:

  • Под Android Java JVM разработана на языке C/C++, а языком программирования V8 является C++, обеспечивающий преобразование между типами данных.Класс стоимостии его подклассы на официальном сайтеAbout V8В нем также четко указано:V8 enables any C++ application to expose its own objects and functions to JavaScript code.. с JavaJNIТо же самое справедливо.

  • В iOS Objective-C является строгим надмножеством языка C, поддерживает C++, а языком программирования JavaScriptCore также является C++, который также обеспечивает преобразование между типами данных.класс JSValue, на его официальном сайте также есть четкое объяснение:

    A JSValue instance is a reference to a JavaScript value. ... You can also use this class to create JavaScript objects that wrap native objects of custom classes or JavaScript functions whose implementations are provided by native methods or blocks.

  • C++ — это почти надмножество C, и это становится ясно, когда вы понимаете взаимодействие между JS, Java и OC. Возьмем в качестве примера V8, например, реализацию функции, которая будет вызываться JS:

    В версии 8 есть два класса шаблонов: ObjectTemplate и FunctionTemplate, которые используются для определения объектов и функций JavaScript.

    // function 定义
    Handle<Value> fn(const Arguments& args){
        //do something
    }
    // JSContext
    Handle<ObjectTemplate> global = ObjectTemplate::New();
    // 将fn Binding到JSContext上,JS便可调用 fn 了
    Handle<FunctionTemplate> fn_template = FunctionTemplate::New(fn);
    global->Set(String::New("fn"), fn_template);
    

Благодаря поддержке канала моста H5 имеет возможности уровня приложения.Давайте поговорим об оптимизации производительности контейнеров — оптимизации сети.


Оптимизация сети

Ключевым моментом для бесперебойной работы нативного Native является то, что большинство статических ресурсов, от которых зависят его страницы, были введены в установочный пакет и установлены в локальной области пользователя вместе с приложением, что экономит накладные расходы на сетевой ввод-вывод. Следуя этому направлению мысли, до того, как 5G станет действительно популярным, потребление сетевых операций ввода-вывода по-прежнему является точкой оптимизации для повышения производительности.В этом разделе кратко обсуждается эта идея.

В основном в двух аспектах:

  1. Статические ресурсы, html/js/css/изображения/шрифты/видео и т. д. предварительно извлекаются или повторно используются в автономном режиме, предварительной загрузке, отложенной загрузке, включении повторного использования кеша WebView и т. д. Когда текущий пользователь получает доступ к странице для загрузки ресурсов, контейнер перехватывает сетевые запросы ресурсов, обращается к автономным или кэшированным файлам ресурсов и использует их. Автономный режим и предварительная загрузка могут напрямую снизить потребление производительности при первом сетевом вводе-выводе, а другие методы могут снизить потребление производительности при втором сетевом вводе-выводе.

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

Для этого уровня оптимизации требуется поддерживающий механизм удаленного управления и опыт управления. Разумный дизайн общего механизма контроля для управления и контроля выпуска статических файлов, обновления версий/исправлений, автономного режима и т. д., а также правил сопоставления для предварительной выборки данных интерфейса, управления жизненным циклом, обработки конфигурации статических/динамических параметров и т. д. .

Мы в основном сосредоточены на реализации оптимизации сети статических ресурсов и кратко представляем идеи реализации, в основном для перехвата сетевых запросов статических ресурсов:

Android

пройти черезshouldInterceptRequestМетод обработки перехвата (системные требования 4.0+), официальная трактовка понятна и ясна:

Notify the host application of a resource request and allow the application to return the data. If the return value is null, the WebView will continue to load the resource as usual. Otherwise, the return response and data will be used.

@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
    WebResourceResponse response = null;
    if(// 匹配拦截规则){
        // 容器生成response
        // WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, Map<String, String> responseHeaders, InputStream data)
        response =  new WebResourceResponse(...);
    }
    return response;
}

iOS

С учетом «официального» фактора реализация iOS относительно сложна, это должны знать студенты, ознакомившиеся с обзором ее релиза, и я его здесь пропущу. . .

Меры предосторожности

Заголовок ответа должен обрабатываться правильно

  1. Чтобы правильно обрабатывать тип содержимого перехваченных ресурсов
  2. Для правильной обработки перехваченных ресурсовAccess-Control-Allow-Origin, чтобы избежать проблем с междоменной проверкой ресурсов

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

В следующем разделе мы кратко поговорим о построении возможностей расширения контейнера ~ идеях реализации поддержки WebAR:


WebAR

Прежде чем говорить о WebAR, давайте взглянем на AR. Кратко: AR получает реальную среду, запуская камеру, передает данные о реальной среде в модуль распознавания и модуль рисования в виде видеокадров, а модуль распознавания передает данные результата распознавания в модуль рисования (обрабатывается в соответствующие данные согласно правилам распознавания бизнес-конфигурации Виртуальные вещи) модуль рисования отображает данные реальной среды и данные результата распознавания после обработки.

WebAR — это две концепции, это Web + AR, которая предоставляет возможности AR на стороне Web. Сам Интернет, полагаясь на рендеринг графики в реальном времени WebGL и возможности обработки видеопотока WebRTC в реальном времени, может обеспечить работу с дополненной реальностью. Однако из-за таких факторов, как разные стандарты операционной среды и низкая производительность рендеринга, мы реализовали поддержку вмешательства в возможности на стороне контейнера, чтобы обеспечить его функции и производительность.

На стороне контейнера возможность AR интегрируется в приложение, а затем возможность AR предоставляется WebView для использования (в сочетании с упомянутой выше возможностью мостового канала), которая представлена ​​в виде страницы H5. Наша сторона Android получает доступ к возможностям дополненной реальности, предоставляемым UCWebView, который визуализируется с использованием WebGL; iOS — это интегрированный ARKit, который достигается путем размещения WebView с прозрачным фоном на полноэкранном OpenGLView.

Оба конца обеспечивают поддержку возможностей модуля идентификации конфигурации. В настоящее время распознавание в основном делится на две категории: распознавание на основе Maker (этот тип обычно используется для идентификации карт маркеров) и распознавание на основе местоположения (популярное понимание состоит в том, чтобы усилить ощущение погружения за счет направление и положение датчика мобильного телефона, например Содержимое, отображаемое в дальнем положении, меньше, и наоборот).

Нынешний апплет просто как небо, и одна из его возможностей — удачное сочетание Native и H5, то есть «отрисовка одного слоя», и мы тоже однажды «поговорим на бумаге». Однако у нас также есть бизнес-требования для создания того же уровня рендеринга и применения собственных компонентов, таких как собственное видео, к WebView для удовлетворения потребностей бизнеса.


Рендеринг того же слоя

Упомянутый здесь рендеринг на том же уровне предназначен для синтеза собственного представления в WebView, и вы можете управлять стилем собственного представления на странице с помощью CSS.

Двухсторонние решения, представленные ниже, основаны на лучших решениях реализации, которые я изучил до сих пор.

Android

Вам необходимо расширить самостоятельно разработанный WebView на основе ядра Chromium. Chromium поддерживает механизм WebPlugin для идентификации и анализа тегов dom. Его идея:

  1. Создайте узел dom в html и укажите тип компонента для идентификации и обработки контейнера.
  2. Chromium создает экземпляр WebPlugin и генерирует RenderLayer, роль которого заключается в создании независимого слоя и возврате соответствующего холста для рисования вида.
  3. Android инициализирует соответствующий нативный компонент в соответствии с идентифицированным типом компонента.
  4. Android рисует представление собственного компонента в SurfaceTexture, связанном RenderLayer (отправляет данные представления Android UI Toolkit в Texture для рисования openGL)
  5. Chromium объединяет этот RenderLayer с представлением веб-страницы.

iOS

Основанный на WKWebView, WK использует многоуровневый метод для внутреннего рендеринга.Как правило, несколько узлов dom объединяются в один слой для рендеринга. Следовательно, между узлами dom и слоями нет однозначного соответствия. Однако, если для свойства CSS узла dom установлено значение «переполнение: прокрутка», WKWebView сгенерирует для него WKChildScrollView,И ядро ​​WebKit обрабатывает иерархические отношения между WKChildScrollView и другими узлами dom., то между узлом dom и слоем существует однозначное соответствие. Поэтому тот же рендеринг слоя можно реализовать на основе WKChildScrollView:

  1. Создайте узел din в html и установите его свойство CSS в значение overflow: прокрутите, укажите тип компонента для идентификации и обработки контейнера.
  2. iOS находит собственный компонент WKChildScrollView, соответствующий узлу dom
  3. iOS инициализирует соответствующий нативный компонент в соответствии с идентифицированным типом компонента.
  4. iOS подключает собственный компонент к узлу WKChildScrollView в качестве своего дочернего представления, так что собственный компонент вставляется в webView.
  5. WebKit завершает рендеринг

Эпилог

Вот первое 👀

У нас также есть унифицированная конструкция URL-адресов контейнеров (URL-адрес может быть динамически настроен и отображаться на таких страницах, как H5 и Native по мере необходимости), создание контейнера мини-программы, создание контейнера Flutter, кэширование моментальных снимков контейнера, предварительный рендеринг, гибридный рендеринг и другие оптимизации, связанные с возможностями Строительство, если вы заинтересованы, приходите 🚀 присоединиться к нам 👊 Есть друзья, которые могут сопровождать вас, чтобы расти счастливо Добро пожаловать в официальный аккаунт Fliggy front-end team, и вы можете связаться с нами напрямую через официальный отчет!