jsBridgeの動作メカニズムを1つの記事で学ぶ

jsBridgeの動作メカニズムを1つの記事で学ぶ

当社のアプリは、フロントエンド ページを埋め込んだ典型的なハイブリッド開発アプリです。ネイティブ ページと同じ効果を実現するには、フロントエンド ページからネイティブ メソッドを呼び出す必要があります。jsBridge jsBridgejs原生コミュニケーションの橋渡しをします。この記事では概念的な内容については触れず、プロジェクトのjsBridgeソース コードを分析して、フロントエンドの観点からどのように実装されているかを理解します。

js 呼び出しメソッド

jsネイティブ メソッドを呼び出す方法を見てみましょう。まず、初期化中にwindow.WebViewJavascriptBridge.initメソッドが呼び出されます。

window.WebViewJavascriptBridge.init()

ネイティブ メソッドを呼び出す場合は、次の関数を使用できます。

関数ネイティブ (funcName, args = {}, callbackFunc, errorCallbackFunc) {
    // パラメータが有効かどうかを確認します if (args && typeof args === 'object' && Object.prototype.toString.call(args).toLowerCase() === '[object object]' && !args.length) {
        args = JSON.stringify(args);
    } それ以外 {
        throw new Error('args は仕様に準拠していません');
    }
    // 携帯電話環境かどうかを判定する if (getIsMobile()) {
        //window.WebViewJavascriptBridgeオブジェクトのcallHandlerメソッドを呼び出す window.WebViewJavascriptBridge.callHandler(
            関数名、
            引数、
            (res) => {
                文字列を JSON に変換します。
                (res.code === 0)の場合{
                    callbackFunc(res) を返します。
                } それ以外 {
                    errorCallbackFunc(res) を返します。
                }
            }
        );
    }
}

メソッド名、パラメータ、および呼び出されるコールバックを渡すだけです。最初にパラメータを検証し、次にwindow.WebViewJavascriptBridge.callHandlerメソッドを呼び出します。

さらに、ネイティブ呼び出しに対してコールバックを提供することもできます。

window.WebViewJavascriptBridge.registerHandler(funcName、callbackFunc);

次に、 window.WebViewJavascriptBridgeオブジェクトとは何かを見てみましょう。

アンドロイド

WebViewJavascriptBridge.jsファイルには、最初にいくつかの変数を定義する自己実行関数が含まれています。

// 変数 varmessagingIframe を定義します。
var sendMessageQueue = [];// メッセージを送信するためのキュー var receivedMessageQueue = [];// メッセージを受信するためのキュー var messageHandlers = {};// メッセージ ハンドラー var CUSTOM_PROTOCOL_SCHEME = 'yy';// カスタム プロトコル var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';

var responseCallbacks = {}; // レスポンスコールバック var uniqueId = 1;

変数名に従って単純に翻訳しましたが、具体的な使用方法は次に分析します。次に、 WebViewJavascriptBridgeオブジェクトが定義されます。

var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
    初期化: 初期化、
    送信: 送信、
    レジスタハンドラ: レジスタハンドラ、
    コールハンドラ: コールハンドラ、
    _fetchQueue: _fetchQueue、
    _handleMessageFromNative: _handleMessageFromNative
};

これは、いくつかのメソッドがマウントされた通常のオブジェクトであることがわかります。ここでは特定のメソッドについては説明しません。以下に続けます。

var doc = ドキュメント;
_createQueueReadyIframe(doc);

_createQueueReadyIframeメソッドが呼び出されます。

関数 _createQueueReadyIframe (ドキュメント) {
    メッセージングIframe = doc.createElement('iframe');
    メッセージングIframe.style.display = 'なし';
    doc.documentElement.appendChild(メッセージングIframe);
}

この方法は非常に簡単で、隠しiframeを作成してページに挿入し、続行するだけです。

// Events タイプのイベント オブジェクトを作成します (基本イベント モジュール) var readyEvent = doc.createEvent('Events');
// イベント名をWebViewJavascriptBridgeReadyとして定義します
readyEvent.initEvent('WebViewJavascriptBridgeReady');
//documentdoc.dispatchEvent(readyEvent); を通じてイベントをトリガーします。

ここでカスタム イベントが定義され、直接ディスパッチされます。他の場所でも、ネイティブ イベントをリッスンするのと同じように、このイベントをリッスンできます。

ドキュメント.addEventListener()
    'WebViewJavascriptBridgeReady'、
    関数 () {
        コンソールログ(window.WebViewJavascriptBridge)
    },
    間違い
);

ここでの目的は、私が理解している限りでは、 jsBridgeファイルが他のコードの後に​​導入される場合、前のコードがwindow.WebViewJavascriptBridge jsBridgeがいつ使用可能になるかを確実に認識できるようにする必要があることです。jsBridge を最初に導入する必要があると規定されている場合、この処理は必要ありません。

自己実行関数はここで終了します。次に、初期のinitメソッドを見てみましょう。

関数 init (messageHandler) {
    (WebViewJavascriptBridge._messageHandler) の場合 {
        新しいエラーをスローします('WebViewJavascriptBridge.init が 2 回呼び出されました');
    }
    // init が呼び出されたときにパラメータが渡されないため、messageHandler=undefined
    WebViewJavascriptBridge._messageHandler = messageHandler;
    // 現在、receiveMessageQueue は単なる空の配列です var receivedMessages = receivedMessageQueue;
    受信メッセージキュー = null;
    (var i = 0; i < receivedMessages.length; i++) の場合 {
        _dispatchMessageFromNative(受信したメッセージ[i]);
    }
}

初期化の観点から見ると、このinitメソッドは何も行わないように見えます。次に、 callHandlerメソッドを見て、Android のメソッドを呼び出す方法を確認しましょう。

関数 callHandler (ハンドラ名、データ、レスポンスコールバック) {
    _doSend({
        ハンドラー名: ハンドラー名、
        データ: データ
    }, レスポンスコールバック);
}

パラメータを処理した後、 _doSendメソッドが再度呼び出されます。

関数_doSend(メッセージ、レスポンスコールバック){
    // コールバックが提供されている場合 if (responseCallback) {
        // 一意のコールバックIDを生成する
        var callbackId = 'cb_' + (uniqueId++) + '_' + 新しい Date().getTime();
        // コールバックは、ID によって responseCallbacks オブジェクトに保存されます。 responseCallbacks[callbackId] = responseCallback;
        // ネイティブに送信するメッセージにコールバック ID を追加します。message.callbackId = callbackId;
    }
    //メッセージをメッセージキューに追加します sendMessageQueue.push(message);
    メッセージングIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}

このメソッドは、ネイティブ メソッドを呼び出すときに、まずコールバック関数の一意のidを生成し、最初に定義したresponseCallbacksオブジェクトに保存し、次に送信情報にidを追加するため、 messageの構造は次のようになります。

{
    ハンドラー名、
    データ、
    コールバックID
}

次に、最初に定義したsendMessageQueue配列にmessageを追加し、最後にiframesrc属性を設定します: yy://__QUEUE_MESSAGE__/は、実際にはカスタム プロトコルurlです。簡単に検索したところ、 nativeがこのurlインターセプトして対応する処理を行うことがわかりました。ネイティブが何を行ったかわからないため、これ以上進むことはできません。簡単に検索したところ、WebViewJavascriptBridge というライブラリを見つけました。弊社では、このライブラリに基づいて修正する必要がありました。インターネット上のいくつかの記事を組み合わせた後、ネイティブがこのurlをインターセプトした後、 jswindow.WebViewJavascriptBridge._fetchQueueメソッドを呼び出すことが大まかにわかりました。

関数_fetchQueue(){
    // 送信するメッセージ キューを文字列に変換します。var messageQueueString = JSON.stringify(sendMessageQueue);
    // メッセージキューをクリアします。 sendMessageQueue = [];
    // Android は返されたデータを直接読み取ることができないため、iframe の src を介して Java と通信します。messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
}

Android はurlをインターセプトした後、 jsが Android にメッセージを送信したことを認識し、 js_fetchQueueメソッドを積極的に呼び出して、以前にキューに追加されたメッセージを取り出します。 jsメソッドによって返されたデータを直接読み取ることはできないため、フォーマットされたメッセージがurlに追加され、 iframeを介して再度送信されます。 このとき、ネイティブはurl yy://return/_fetchQueue/インターセプトし、後続のメッセージを取り出し、実行するネイティブ メソッド名とパラメーターを解析して、対応するネイティブ メソッドを実行します。 ネイティブ メソッドが実行されると、 jswindow.WebViewJavascriptBridge._handleMessageFromNativeメソッドを積極的に呼び出します。

関数_handleMessageFromNative(messageJSON) {
    // 前の init メソッドのロジックによれば、receiveMessageQueue は null に設定されるため、else ブランチに進みます if (receiveMessageQueue) {
        メッセージキューを受信します。push(messageJSON);
    } それ以外 {
        _dispatchMessageFromNative(メッセージJSON);
    }
}

_dispatchMessageFromNativeメソッドが何を行うか見てみましょう:

関数_dispatchMessageFromNative(messageJSON) {
    setTimeout(関数() {
        // 返送された元のメッセージは文字列型で、JSON に変換されます
        var message = JSON.parse(messageJSON);
        var レスポンスコールバック;
        // Java 呼び出しが完了し、返された responseId は、前に送信した callbackId です。
        if (メッセージ.レスポンスID) {
            // responseCallbacks オブジェクトから ID に関連付けられたコールバック メソッドを取得します。 responseCallback = responseCallbacks[message.responseId];
            if (!レスポンスコールバック) {
                戻る;
            }
            // コールバックを実行し、js が Android メソッドを呼び出してメッセージを正常に受信します。 responseCallback(message.responseData);
            responseCallbacks[message.responseId]を削除します。
        } それ以外 {
            // ...
        }
    });
}

messageJSONはネイティブ メソッドによって返されるメッセージです。ネイティブ メソッドの実行後に返される関連情報に加えて、前に渡したcallbackIdも含まれているため、このidを使用してresponseCallbacks内の関連するコールバックを見つけて実行できます。これで、 jsがネイティブ メソッドを呼び出すプロセスは終了です。ただし、 idが存在しない場合に関数に分岐があることは明らかです。これは何に使用されますか? 先ほど紹介したのはすべてjsネイティブ メソッドを呼び出すことですが、明らかにネイティブは、リターン キー関数の一般的なインターセプションなど、 jsに直接メッセージを送信することもできます。ネイティブがリターン キー イベントをリッスンすると、フロントエンド ページに通知する情報を積極的に送信し、ページは対応するロジックを実行できます。このelse分岐は、この状況を処理するために使用されます。

関数_dispatchMessageFromNative(messageJSON) {
    setTimeout(関数() {
        if (メッセージ.レスポンスID) {
            // ...
        } それ以外 {
            // ネイティブに送信するメッセージに ID があるように、ネイティブから送信されるメッセージにも ID があり、ネイティブ内部ではこの ID にコールバックを関連付けます if (message.callbackId) {
                var コールバックレスポンスID = message.callbackID;
                //フロントエンドがネイティブデバイスに応答する必要がある場合は、ネイティブデバイスが ID を通じて対応するコールバックを見つけて実行できるように、ネイティブデバイスによって事前に送信された ID を含める必要があります。responseCallback = function (responseData) {
                    _doSend({
                        レスポンスID: コールバックレスポンスID、
                        レスポンスデータ: レスポンスデータ
                    });
                };
            }
            // デフォルトの _messageHandler を設定していないため、未定義です
            var ハンドラー = WebViewJavascriptBridge._messageHandler;
            // ネイティブに送信されるメッセージには処理メソッドの名前が含まれます if (message.handlerName) {
                // メソッド名を使用して、messageHandlers オブジェクト内に対応する処理メソッドがあるかどうかを確認します。handler = messageHandlers[message.handlerName];
            }
            試す {
                //処理メソッドを実行します。handler(message.data, responseCallback);
            } キャッチ(例外){
                if (typeof コンソール !== 'undefined') {
                    console.log('WebViewJavascriptBridge: 警告: JavaScript ハンドラーが例外をスローしました。', message, exception);
                }
            }
        }
    });
}

たとえば、ネイティブのリターン キー イベントをリッスンする場合は、まずwindow.WebViewJavascriptBridgeオブジェクトのメソッドを通じてそれを登録します。

window.WebViewJavascriptBridge.registerHandler('onBackPressed', () => {
    // 何かをする...
})

registerHandlerメソッドは次のとおりです。

関数registerHandler(ハンドラ名、ハンドラ) {
    messageHandlers[ハンドラー名] = ハンドラー;
}

とても簡単です。監視するイベント名とメソッドをmessageHandlersオブジェクトに保存します。次に、ネイティブ モニターがリターン キー イベントを受信すると、次の構造のメッセージを送信します。

{
    ハンドラー名: 'onBackPressed'
}

このようにして、 handlerNameを通じて登録した関数を見つけて実行することができます。

この時点で、Android 環境におけるjsとネイティブの相互呼び出しのロジックは終了しました。まとめると、

1.jsはネイティブを呼び出す

一意のidを生成し、コールバックとidを保存し、送信する情報 (今回生成された一意の id を含む) をキューに追加し、 iframeを通じてカスタム プロトコル リクエストを送信します。ネイティブ インターセプト後、 js window.WebViewJavascriptBridgeオブジェクトのメソッドを呼び出してキュー情報を取得し、リクエストとパラメータを解析して対応するネイティブ メソッドを実行し、指定されたjs window.WebViewJavascriptBridgeメソッドを呼び出して、レスポンス (フロントエンドから送信された id を含む) をフロントエンドに渡します。フロントエンドは、 idを通じて以前保存したコールバックを見つけて実行します。

2. ネイティブコールjs

まず、フロントエンドは監視するイベントを事前に登録し、イベント名とコールバックを保存する必要があります。その後、ネイティブは特定の時間にjs window.WebViewJavascriptBridgeオブジェクトの指定されたメソッドを呼び出します。フロントエンドは、戻りパラメータのイベント名に従って登録されたコールバックを見つけて実行します。同時に、ネイティブはidも渡します。フロントエンドが対応するロジックを実行した後にネイティブにメッセージを送信する必要がある場合は、 idを返す必要があり、ネイティブはidに基づいて対応するコールバックを見つけて実行します。

ご覧のとおり、 jsとネイティブ側の両方のロジックは一貫しています。

iOS版

iosと Android は基本的に同じですが、細かい点では若干の違いがあります。まず、プロトコルが異なります。iOS ios場合は次のようになります。

var CUSTOM_PROTOCOL_SCHEME_IOS = 'https';
var QUEUE_HAS_MESSAGE_IOS = '__wvjb_queue_message__';

次に、 ios iframeを初期化して作成すると、リクエストが送信されます。

var BRIDGE_LOADED_IOS = '__bridge_loaded__';
関数 _createQueueReadyIframe (ドキュメント) {
    メッセージングIframe = doc.createElement('iframe');
    メッセージングIframe.style.display = 'なし';
    if (isIphone()) {
        // これは iOS が最初にロードする必要があるブリッジです
        : メッセージングIframe.src = CUSTOM_PROTOCOL_SCHEME_IOS + '://' + BRIDGE_LOADED_IOS;
    }
    doc.documentElement.appendChild(メッセージングIframe);
}

次に、 iosメッセージ キューを取得するときに、 iframeを経由する必要がなくなります。 js関数を実行することで、返されたデータを直接取得できます。

関数_fetchQueue(){
    var messageQueueString = JSON.stringify(sendMessageQueue);
    送信メッセージキュー = [];
    return messageQueueString; // iframeを経由せず直接返す
}

その他はすべて同じです。

要約する

この記事は、 jsBridgeのソースコードを分析して、実はこれが非常に単純なものであることを発見しました。しかし、普段は真剣に勉強したことがないかもしれません。いつも「大きなこと」をやりたいので、「高尚な」人になってしまうのです。私のようにならないことを祈ります。

jsBridgeの動作の仕組みを1つの記事で学ぶこの記事はこれで終わりです。jsBridgeの動作の仕組みに関するより関連性の高いコンテンツについては、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも123WORDPRESS.COMをよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScriptの動作メカニズムの詳細な説明とイベントループについての簡単な説明
  • JavaScriptの動作原理を理解しましょう
  • Dockerのインストール、イメージの作成、NodeJSプログラムの読み込みと実行の詳細なプロセス
  • Jupyter Notebook で JavaScript を実行する方法
  • ノードターミナルでjsファイルを実行するとES6構文がサポートされないという問題を解決します
  • Visual Studio Code で HTML、CSS、JS ファイルをコンパイルして実行するチュートリアル
  • GolangでJavaScriptを実行する例
  • フロントエンドJavaScriptの動作原理

<<:  MySQLデータベースのマスタースレーブ同期の実際のプロセスの詳細な説明

>>:  MySQL データベースの基礎を始めるための一般的なコマンドの概要

推薦する

CSSに基づいてマウス入力の方向を決定する

以前、フロントエンド技術グループに所属していたとき、グループのメンバーが面接中に問題に遭遇したと言っ...

Centos8 で yum を使用して mongodb 4.2 をインストールする方法

1. リポジトリファイルを作成するmongodb の公式インストール ドキュメントを参照し、次のスク...

ReactでCSSをエレガントに書く方法

目次1. インラインスタイル2. インポート方法を使用する3.cssモジュールのエクスポート4. ス...

OpenSSL は双方向認証のチュートリアルを実装します (サーバーとクライアントのコード付き)

1. 背景1.1 問題点最近の製品テスト レポートでは、PKI ベースの認証方法の使用が推奨されて...

HTTP および HTTP コラボレーション Web サーバー アクセス フロー図

Web サーバーは、独立したドメイン名を持つ複数の Web サイトを構築できるほか、通信経路上のトラ...

フロントエンドコンポーネント化の基礎知識を詳しく解説

目次コンポーネントの基本概念オブジェクトとコンポーネントの違い成分属性属性とプロパティ属性:財産:ク...

React コンポーネント間で通信する 3 つの方法 (シンプルで使いやすい)

目次1. 親子コンポーネント通信2. クロスレベルコンポーネント通信1. レイヤーごとに値を渡す2....

Linux で MySQL スケジュールタスクを実装する方法

前提: ストアド プロシージャは、毎日午後 10 時から午前 5 時まで 10 分ごとに実行されます...

jsは前のページに戻り、コードを更新します

1. Javascript は前のページ history.go(-1) に戻り、2 つのページを返し...

MySQLの通常インデックスとユニークインデックスの違いの詳しい説明

目次1 概念上の区別2 事例紹介3 クエリパフォーマンス4 アップデートのパフォーマンス4.1 記憶...

VUE ユニアプリライフサイクルに関する簡単な説明

目次1. アプリケーションライフサイクル2. ページのライフサイクルコンポーネントライフサイクル要約...

MySQL Community Server 8.0.12 のインストールと設定方法のグラフィックチュートリアル

MySQL 8 は、NoSQL、JSON などのサポートなど、まったく新しいエクスペリエンスをもたら...

Linux Centos8 CA証明書作成チュートリアル

必要なファイルをインストールする Yum インストール openssl-* -yデータベースインデッ...

Node.js のフロントエンドとバックエンドのインタラクションによるユーザーログインの実装の実践

目次1. プロジェクト要件次にコーディングを始める1. フロントエンドページを作成する(CSSスタイ...

alpineをベースにdockerfileで作成したクローラーScrapyイメージの実装

1.アルパインイメージをダウンロードする [root@DockerBrian ~]# docker ...