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 データベースの基礎を始めるための一般的なコマンドの概要

推薦する

Vueはユーザーログインとトークン検証を実装します

フロントエンドとバックエンドを完全に分離する場合、Vue プロジェクトでトークン検証を実装する一般的...

vuexプロジェクトにおけるログインステータス管理の実践プロセス

目次道具:ログインシナリオ:練習する:シナリオ1: 思考と実践シナリオ2: 思考と実践要約する道具:...

CentOS7 で ethereum/Ethereum を最初からインストールする

目次序文sudo書き込み権限を追加するgit 2.9.0をインストールopenssl 1.1.1l ...

Mysql5.7 のグループ連結関数を使用するときにデータが切り捨てられる問題に対する完璧な解決策

一昨日、本番環境でGROUP_CONCAT関数を使用して選択したデータが切り捨てられ、最大長が102...

MySQL マスタースレーブ同期メカニズムと同期遅延問題追跡プロセス

序文DBA として、仕事中に MySQL マスターとスレーブの同期遅延の問題に遭遇することがよくあり...

MySQL での実行計画の詳細分析

序文効率的なSQL文の書き方は、Explain実行計画の分析と切り離せません。実行計画とは何か、効率...

OpenLayers 3 のベクターマップソースの読み込みの問題を解決する

1. ベクターマップベクター グラフィックスは直線と曲線を使用してグラフィックスを表します。これらの...

VMWare を使用して Windows 上で Linux 環境を構築する手順 (画像とテキスト)

Mac を返却して以来、元のラップトップは使用されていません。このラップトップの構成は非常に良好で...

シンプルなウェブデザインコンセプトのカラーマッチング

(I)ウェブページのカラーマッチングの基本概念(1)白黒の言葉は永遠のテーマです。誰もそれを悪く言う...

JavaScriptアニメーション関数のカプセル化の詳細な説明

目次1. アニメーション機能の原理2. アニメーション関数のシンプルなカプセル化3. アニメーション...

MySQL 5.7.15 バージョンのインストールと設定方法のグラフィックチュートリアル

この記事では、MySQLバージョン5.7のインストール方法と使用方法、およびデータベースデータの保存...

MySQLは、統計クエリを最適化するために、sum、case、whenを巧みに使用します。

私は最近、会社で統計レポートの開発に関わるプロジェクトに取り組んでいました。データの量が比較的多かっ...

nginx-ingress-controller ログ永続化ソリューションのソリューション

最近、nginx-ingress-controller のアプリケーションについて説明した公開アカウ...

UbuntuへのDocker CEのインストール

この記事は、Ubuntu 17.10 での Docker CE のインストールを記録するために使用さ...

Ubuntu 18.04.4 に MySQL をインストールするプロセスの詳細な説明

Ubuntu 18.04.4 に MySQL をインストールするプロセスを見てみましょう。内容は次の...