DoKit:1台のマシンに複数のコントロールを搭載 WebViewによるJSの非侵襲的インジェクション|DiDiオープンソース

Didi テクノロジーを「スター ⭐️」に設定します

記事の更新情報をできるだけ早く受け取る

ガイド

2018 年の公式オープンソース以来、DoKit は 5 年間の磨きをかけ、6 つの主要なプラットフォーム (Android、iOS、Web、ミニ プログラム、Flutter、PC) をサポートする比較的完全なエコシステムに発展しました。 Didi オープンソース委員会、インキュベーション プロジェクト。対外的には、多くの大手企業で採用されており、高い評価を得ています。

Didi 社内では、DoKit がすべてのビジネスラインとアプリに導入されており、基本的に日常の開発とテストのすべてのシナリオをカバーし、研究開発と優秀な学生の日常の作業効率を向上させています。しかし、Didi の社内クロスターミナル R&D ソリューションの普及により、R&D 効率が大幅に向上する一方で、品質チームへのプレッシャーも大きくなりましたしたがって、DoKit はクロスターミナル R&D ソリューションの双子の兄弟として、2023 年も引き続き UI 自動テスト、複数のコントロールを備えた 1 台のマシン、その他のテクノロジーなどのテスト効率の分野に注力していきます。同時に、複数の制御を備えた 1 台のマシンの開発で遭遇する技術的な困難と解決策も開発者と共有します。

この記事は次のように分かれています。

1. 複数の制御装置を備えた 1 台のマシンで発生する 2 つの大きな問題

2. 従来のJSインジェクション手法とは何ですか?

  • loadUrlを使用して挿入する

  • EvaluateJavascript を使用して挿入する

  • HTML を変更して <script> タグ インジェクションを挿入する

3. 最初の行が実行されるのはなぜですか?

  • 失敗経験

  • 最初の旅行が必要

  • html 挿入 <script> タグの挿入

4. HTML 挿入 <script> タグ インジェクションを実装するにはどうすればよいですか?

  • <script> タグを挿入すると最初の行が実行できるのはなぜですか?

  • HTML ファイルの読み込みをインターセプトするにはどうすればよいですか?

  • HTMLファイルに<script>タグを挿入するにはどうすればよいですか?

5. 非侵入的なコードを実現するにはどうすればよいですか?

  • WebViewClientProxy の非侵入的な設定

  • コードを挿入するタイミング

  • WebViewClientProxy を設定する

  • 互換性の問題の処理

6. まとめ

モバイル開発中に、開発者は次の 3 つの問題に遭遇することがよくあります。

1. 開発プロセスでは、デバッグやテストに便利な多くの開発ツールを一時的に開発する必要があります。

2. 開発ツールには入り口がたくさんあり、目的の機能がどこにあるのかわからないことがよくあります。

3. デザイン学生は、UIをチェックして承認する際に、UIがデザインドラフトの要件を満たしているかどうかを確認するための煩雑な操作手順を必要とします...

日々誰もが悩まされているこれら 3 つの問題を解決するために、DoKit チームは 30 を超える基本的な研究開発効率化ツール (多くのテストおよび視覚的効率化ツールを含む) を開発し、それらすべてが 1 つのポータルに集められています。見やすくて便利、探してください。同時に、日々の開発プロセスにおけるツール入口の散在して乱雑な問題やツール開発コストの高さの問題を解決するために、DoKitはビジネスと関連の強いカスタム拡張ツールなどを統合する高度なカスタマイズ機能も開発しました。モバイル端末の開発者にとって良い仕事をしたいと考えています、研究開発効率化ツール。

1. 複数の制御装置を備えた 1 台のマシンで発生する 2 つの大きな問題

DoKit は複数の制御を備えたデバイスで、簡単に言うと、1 台の携帯電話を操作することで、複数の携帯電話を制御して同時に作業を実行できる機能であり、特にマルチテストの面で品質チームのテスト効率の向上を目的としています。デバイスの互換性テスト。この 1 年間、継続的に開発と改良を行ってきましたが、その機能は比較的完成度が高く、安定しています。ネイティブ側で 1 台のマシンで複数のコントロールを使用する機能と、1 台のマシンで複数のコントロールを使用する H5 の機能を完了した後、H5 の 1 台のマシンで複数のコントロールを使用する機能を使用するには、 dokit-for-web.js を導入する必要があることがわかりました。マシンマルチコントロール機能を持​​たせるために、テスト環境で H5 ページを公開するときにファイルを作成します。

ネイティブ側では、DoKit 依存関係パッケージを導入することで、複数のコントロールを持つ 1 台のマシンもコンパイルおよび初期化されますが、アプリ ビジネス プロセスに H5 ページがある場合、ネイティブと H5 の場合、複数のコントロールを持つ 1 台のマシンを有効にすることはできませ。同時に、テスト プロセスが H5 ページに遭遇したため、1 台のマシンと複数のコントロールの制御プロセスが中断され、通常の同期動作が失敗します。

9b0fd31e919e49e21c037d312598979b.png

(DoKit 1 台のマシンのマルチコントロール デモのスクリーンショット)

H5 に複数のコントロールを備えた 1 台のマシンをサポートさせるのは難しくありません。dokit-for-web を作成するだけで済みます。マシンのマルチコントロール コードをオンラインの正式な環境に取り込んで実行します。

ネイティブアプリのデバッグパッケージでは、異なる動作環境(テスト環境/正式環境)をいつでも切り替えることができます。そのため、アプリを正式環境に切り替えると、H5 マルチコントロール機能が使用できなくなります。

スムーズなユーザー エクスペリエンスを確保し、 H5 マルチ コントローラーをテスト環境と公式環境で同時に実行できるようにするにはH5 マルチ コントローラーをネイティブ側と同じ環境スイッチで制御できるようにする必要があります。 。しかし、Webフロントエンド研究開発システムとクライアントには違いがあり、テスト環境導入サービスはあるものの、H5ページが多数・散在していることや、H5ページが大量に存在すること、H5をワンクリックでテスト環境に切り替えることには困難が多くあります。チーム調整コスト。

したがって、クライアントを介して dokit-for-web.js コードを挿入することは理想的な選択となり、環境の同期切り替えの問題を解決できるだけでなく、H5 公式環境サービスが環境サービスを統合できないという問題も解決できます。複数の制御コードを持つマシンの問題。

継続的な調査と JavaScript コードを WebView に挿入する試みを通じて、JavaScript コードの挿入を完了するための DoKit 設計概念に準拠する一連の実装メソッドを確立しました。

DoKit が Android 側の WebView に JavaScript を挿入する探索と実践のプロセスを見てみましょう。

2. 従来のJSインジェクション手法とは何ですか?

情報を検索およびクエリするために、一般的に使用される注入スキームは、WebView で WebViewClient を設定し、WebViewClient を介して onPageFinished() でコールバックすることです。onPageStart() の後で、loadUrl( を介して注入する必要がある JS コードをロードできます) ) またはevaluateJavascript()、または直接 HTML ファイルを変更して <script> タグを挿入し、JS コードを挿入します。

loadUrlを使用して挿入する

WebView によって提供されるloadUrl を使用して、JS コードを挿入します。この API は、Android システムでサポートされている最も古い API であり、Android のすべてのバージョンで使用できます。WebView の js 実行サポート スイッチをオンにする必要があります。

サンプルコード:

//打开js运行支持开关
webView.getSettings().setJavaScriptEnabled(true);


public void onPageFinished(WebView webView, String url) {
    webView.loadUrl("javascript:javacalljs()"); 
    supper.onPageFinished(view, url);
}

EvaluateJavascript を使用して挿入する

WebView の EvaluateJavascript を使用して JS コードを挿入します。この API は Android 4.4 以降で利用できることに注意してください。結果を取得するために複雑なコールバックメソッドを経由する必要があるloadUrlに比べて、evaluateJavascriptはコードを挿入した後に実行結果を直接返すことができるため、戻り値が必要な場合に非常に便利です。

サンプルコード:

public void onPageFinished(WebView webView, String url) {
    //在android4.4及以上使用
    webView.evaluateJavascript("javacalljs()", new ValueCallback<String>  () {
        @Override
        public void onReceiveValue(String value) {
            //todo 执行js方法的返回结果
        }
    })
}

HTML を変更して <script> タグ インジェクションを挿入する

HTML を変更して <script> タグ挿入を挿入することは、純粋なフロントエンド JS インジェクション方法であり、コードを直接記述して JS コードを追加することと同等です。これは通常、ネットワークの傍受や変更を通じて悪意のあるコードが挿入されるいくつかのシナリオで使用されます。この方法は、ネットワーク通信を傍受できる限り、ブラウザ API によって制限されません。

通常のhtmlファイルは以下の通りです。

<html>
<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <script type="text/javascript" src="https://dokit.cn/js/h.js/></script>
</head>
<body>
    <div class="main">...</div>
    <div class="pan">....</div>
    <script type="text/javascript" src="https://dokit.cn/js/a.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/b.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/c.js/"/> <script>
</body>
</html>

変更後のhtmlファイルは以下の通りです。

<html>
<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <script type="text/javascript" src="https://dokit.cn/js/h.js/></script>
</head>
<body>
    <div class="main">...</div>
    <div class="pan">....</div>
    <script type="text/javascript" src="https://dokit.cn/js/a.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/b.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/c.js/"/> <script>
    //以下是注入代码
    <script>javacalljs()</script>
</body>
</html>

3. 最初の行が実行されるのはなぜですか?

失敗経験

当初は、loadUrl と EvaluateJavascript を使用して、1 台のマシンに複数のコントロールを含む JS コードを挿入していましたが、テスト ページでの実行後はすぐにテストに合格しましたが、実際のビジネス シナリオに移行すると多くの問題が明らかになりました。調査の結果、すべてのネットワーク要求が傍受されたわけではなく、マスターからの一部の要求データをスレーブに同期できず、その結果、スレーブがデータをロードしてページを正常に表示できなくなったり、一部のイベントが正常に表示されなくなったりすることが判明しました。傍受される。

最終的な判断は、テスト ページが単純すぎて実際の使用シナリオを考慮していないということです。通常、ビジネス側ではさまざまなフレームワークが使用され、これらのフレームワークは通常、最初に実行されることを保証するいくつかの操作を実行します。これは、複数のコントロールを持つ 1 台のマシンのフックの失敗につながります。一方で、ビジネスで使用されるフレームワークは互換性があり、適合する必要があります。他方、フレームワーク内の一部の操作はフック ポイントに影響を与えることが多く、したがって、事前にキーポイントにフックするために、複数のコントロールを持つ 1 台のマシンのコードを最初に実行する必要があります。

最初の行を実行する必要があります

インジェクション コードが最初に実行されることが保証できない場合、インジェクションに必要な一部のフック ポイントが正しく動作しません。例えば、ネットワークリクエストの傍受が早期に完了できないため、リクエストされたデータの一部が傍受されず、マスターとスレーブ間のデータ同期が実現できなくなります。ページがレンダリングされると、ページのレンダリングがインターセプトされ、ページ上の一部のユーザー操作イベントをフックできなくなります。

WebView が提供する機能を使用して、loadUrl と EvaluateJavascript を介して 1 台のマシンに複数のコントロールを含む JS コードを挿入し、これらのインターフェイスを使用してコードを挿入します。挿入されたコードが正常に実行されることを保証するための正確な注入時間はありません。他の JS コード。実行前。

html 挿入 <script> タグの挿入

フロントエンドのクラスメートと通信した後、コード インジェクションの最初の行を実装するには、フロントエンド メソッドを通じてインジェクションを実装する必要があります。魔法のように改造された独自のブラウザを使用しない限り、WebView にはビジネス ニーズを満たす方法がありません。このような機能を提供するエンジン。

挿入された <script> タグを使用すると、最初の行が実行される基礎が保証され、WebView の設計はシングルスレッド モデルを採用します。つまり、マルチスレッド API を使用しない場合、Web ページの読み込みと JS の実行が順番に実行されます。

HTML の設計ルールに従っている限り、挿入された <script> タグがいつ実行されるかを正確に制御できます。

4. HTML挿入<script>タグインジェクションの実装方法

なぜ実行の最初の行が実現できるのでしょうか?

前述の WebView で採用されているシングルスレッド モデルに加えて、HTML によるページの読み込みには一定のルールがあります。html が読み込まれると、最初に <head> タグが読み込まれ、次に <body> タグが読み込まれます。

<head>タグ内に<script>タグがあった場合、外部JSファイルをインポートするかJSコードをインポートするかを判断し、ファイルの場合は外部ファイルのダウンロードを開始し、コードの場合は外部ファイルのダウンロードを開始します。 HTML ページの読み込みは一時停止されます。このとき、JavaScript エンジンはコードの実行を開始し、JS コードの実行後、他のタグ内のコンテンツの読み込みを続けます。<head> タグの内容が読み込まれるまで待ってから、<body> タグの内容を読み込みます。

<body> タグ内で <script> タグが見つかった場合は、<head> タグと同じ方法で処理されます。インポートが外部JSファイルであるかJSコードであるかを判断します。外部ファイルの場合は、外部ファイルのダウンロードを開始します。コードの場合は、ページの読み込みを一時停止し、JavaScript エンジンにコードの実行を開始させます。JS コードの実行後、引き続き実行されます。ページをロードします。

したがって、挿入された <script> タグは <head> タグ内に挿入され、<head> タグの前に挿入され、JS ファイルではなくコードとして挿入される必要があります。

サンプルコード:

<html>
<head>
    //以下是注入代码,必须放在head的起始位置
    <script>javacalljs()</script>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <script type="text/javascript" src="https://dokit.cn/js/h.js/></script>
</head>
<body>
    <div class="main">...</div>
    <div class="pan">....</div>
    <script type="text/javascript" src="https://dokit.cn/js/a.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/b.js/"/> <script>
    <script type="text/javascript" src="https://dokit.cn/js/c.js/"/> <script>
    //以下是注入代码,在这里的不会被首先执行
    <script>javacalljs()</script>
</body>
</html>

HTML ファイルの読み込みをインターセプトするにはどうすればよいですか?

WebView と WebViewClient のコードを見ると、WebView が送信するネットワーク リクエストを傍受できることがわかり、WebViewClient の shouldOverrideUrlLoading() メソッドを通じて、HTML ファイルのロード リクエストを含む Webview のすべてのネットワーク リクエストを傍受できます。

webRequest.isForMainFrame()を使用してhtmlファイルへのリクエストを識別し、URLの下位バージョンについてはファイルの拡張子が.htmlであるかどうかで判断できます。リクエストをインターセプトした後、返された WebResourceResponse を直接使用することも、OkHttp リクエストなどの独自のネットワーク ライブラリを通じて HTML ファイルにロードすることもできます。HTML ファイルを要求するには、現在のスレッドをブロックする必要があることに注意してください。

サンプルコード:

// 拦截网络请求的回调,在android 21以后被废弃
public WebResourceResponse shouldInterceptRequest(WebView view, String request) {
    if(DoKitUtils.isHtmlRequest(request)){
      //todo 拦截代理请求html并完成JS代码注入
      return DoKitUtils.hookWebViewHtmlRequest(request);
    }
    return supper.shouldInterceptRequest(view, request)
}

サンプルコード:

// 拦截网络请求的回调,在android 21新增加
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
    if(request.isForMainFrame()){
      //todo 拦截代理请求html并完成JS代码注入
      return DoKitUtils.hookWebViewHtmlRequest(request);
    }
    return supper.shouldInterceptRequest(view, request)
}

HTMLファイルに<script>タグを挿入するにはどうすればよいですか?

HTML テキストを解析する

WebViewClient が提供するコールバック shouldInterceptRequest() は、HTML ファイルへのネットワーク リクエストをインターセプトしてファイルを取得します。HTML ファイルを解析して、挿入された <script> タグを目的の挿入位置に挿入する必要があります。

XML 解析ライブラリを使用して HTML ファイルを解析する場合は、より複雑になり、目的のタグを直接検索して変更することはできません。DoKit は Jsoup を使用して HTML テキストを解析します。

サンプルコード:

pulic String handleWebViewHtml(String html){
    Document document = Jsoup.parse(html);
    //todo insert dokit <script>
    return document.toString();
}

<script>タグを挿入

HTML ファイルを取得し、HTML 解析ライブラリ Jsoup を通じて HTML ファイルを解析します。次に、HTML 内の <head> タグを見つけて、複数のコントロールを持つ 1 台のマシンに挿入する必要がある <script> タグ コードを挿入する必要があります。タグの始まり。最後に、 shouldInterceptRequest() コールバックに戻る WebResourceResponse オブジェクトを構築します。

サンプルコード:

/**
 * 拦截html请求并注入JS代码
 */
public static WebResourceResponse hookWebViewHtmlRequest(WebResourceRequest request){
    Response response = requestWebViewHtml(request);
    if(response !=null){
        String html = injectScript(toHtml(response));
        return WebResourceResponse("text/html",
            response.header("content-encoding", "utf-8"),
            ConvertUtils.string2InputStream(html, "utf-8")
    }
    return null;
}


/**
 * 插入script标签
 */
private static String injectScript(String html){
    //读取本地js hook 代码
    String dokitScript = ResourceUtils.readAssets2String("dokit/dokit_script.html")
    Document doc = Jsoup.parse(html)
    doc.outputSettings().prettyPrint(true)
    val elements = doc.getElementsByTag("head")
    if (elements.size > 0) {
        elements[0].prepend(dokitScript)
    }
    return doc.toString()
}

5. 非侵入的なコードを実現するにはどうすればよいですか?

最初は、WebViewClient を設定するのは簡単だと思いました。WebView を作成した後に setWebViewClient() を呼び出して WebViewClientProxy を設定するだけです。バイトコードの変更は、DoKit プロジェクトの多くの機能で使用されています。また、WebView のバイトコード変更に関するリファレンスがすでに存在します。実際の運用では、完全に非侵入的なバイトコード変更を実現するには、まだ注意すべき箇所がいくつかあることがわかります。

WebViewClientProxy の非侵入的な設定

まず第一に、コードへの侵入を禁止する必要があります (DoKit 関数の一貫したスタイル、コードは侵入的ではなく、いつでも使用または削除できます)。そのため、アクセス側が WebViewClientProxy をアクティブに設定することを許可できません。 Weview を作成した後。DoKit でより多くのシナリオを使用する成熟したテクノロジーを選択し、ASM を使用して変換中にバイトコードを変更し、WebView の WebViewClientProxy 設定を実装します。

WebViewClientProxy の設定を実装するようにバイトコードを変更するには、DoKit プラグインを使用し、プラグインのスイッチがオンになっていることを確認する必要があります。

コードを挿入するタイミング

最初の挿入時間

最初の挿入機会は WebViewClient の作成時であり、WebViewClient はコンストラクターの呼び出し時に ASM 挿入コードを通じて設定されます。ただし、WebView はシステム クラスであるため、コンパイルされた製品には含まれず、独自に記述されたクラスでもないため、コンストラクターにコードを挿入して WebViewClient を設定することはできません。そして通常、WebViewには業務側が独自のWebViewClientを設定することになりますが、コンストラクタやWebView作成時にWebViewClientを設定しても上書きされて設定に失敗する危険性があります。コンストラクターで WebViewClient を設定するだけでは十分ではありません。

サンプルコード:

public class WebView{
  //构造函数
  public WebView(Context contex){
    setWebViewClient(new WebViewClientProxy())
  }
}

2回目の挿入時間

2 番目の挿入方法は、setWebViewClient() の実行時に ASM を通じて setWebViewClient 関数を変更し、WebViewClientProxy を設定するコードを挿入することです。また、WebView はシステム クラスであるため、コンパイルされた製品には含まれず、独自に記述されたクラスでもないため、バイトコードを変更して変更することはできません。また、すべての WebView が WebViewClient を設定するという保証はないため、挿入が成功するという保証もありません。WebView が独自の WebViewClient を設定しない場合、WebViewClientProxy の設定は失敗するため、WebView を変更できたとしても、すべてのシナリオをカバーできることは保証できません。

サンプルコード:

public void setWebViewClient(WebViewClient webViewClient){
  WebViewClientProxy = new WebViewClientProxy(webViewClient);
  super.setWebViewClient(WebViewClientProxy);
}

3回目の挿入時間

3 番目の挿入機会は、WebView のloadUrl() メソッドが呼び出されてページまたは JS コードが読み込まれるときです。loadUrl の前に、Webview の準備ができた後にする必要があります。WebViewClient をビジネス コードに設定する必要がある場合は、WebViewClient をloadUrl の前に設定する必要があります。WebViewClientProxy を設定すると、既に設定されている WebViewClient を取得して、ビジネス機能に影響を与えることなくプロキシを実装できます。loadUrl は、WebViewClientProxy を設定する最後の機会でもあり、ページをロードするために呼び出す必要があるメソッドでもあります。

ただし、ここにも問題があり、WeView のloadUrl メソッドのバイトコードを直接変更して、WebViewClientProxy のロジックを挿入および設定することはできません。

WebViewのloadUrlメソッドを直接変更することはできないため、すべてのloadUrlが呼び出されるバイトコードのみを変更できます。コードのすべての行をスキャンする必要がありますが、この方法でバイトコードを変更する場合は比較的非効率的です。アクセス側がカスタム WebView を使用してloadUrl メソッドを書き換えることができれば、すべてをスキャンすることによって発生する比較的大きなバイトコードの時間のかかる処理を回避できます。

サンプルコード:

klass.methods.forEach { method ->
    method.instructions?.iterator()?.asIterable()
        ?.filterIsInstance(MethodInsnNode::class.java)?.filter {
            if ("loadUrl".equals(it.name)) {
                "hook loadUrl() all ${className} ^${superName}^${it.owner} :: ${it.name} , ${it.desc} ,${it.opcode}".println()
            }
            (it.opcode == Opcodes.INVOKEVIRTUAL || it.opcode == Opcodes.INVOKESPECIAL)
                && it.name == "loadUrl"
                && (it.desc == "(Ljava/lang/String;)V" || it.desc == "(Ljava/lang/String;Ljava/util/Map;)V")
                && isWebViewOwnerNameMatched(it.owner)
        }?.forEach {
            "${context.projectDir.lastPath()}->hook WebView#loadurl method  succeed in :  ${className}_${method.name}_${method.desc} | ${it.owner}".println()
            if (it.desc == "(Ljava/lang/String;)V") {
                method.instructions.insertBefore(it, createWebViewInsnList())
            } else {
                method.instructions.insertBefore(it, createWebViewInsnList(method))
            }
        }
}

サンプルコード:

/**
 * 创建webView函数指令集
 * 参考:https://www.jianshu.com/p/7d623f441bed
 */
private fun createWebViewInsnList(): InsnList {
    return with(InsnList()) {
        //复制栈顶的2个指令 指令集变为 比如 aload 2 aload0 / aload 2 aload0
        add(InsnNode(Opcodes.DUP2))
        //抛出最上面的指令 指令集变为 aload 2 aload0 / aload 2  其中 aload 2即为我们所需要的对象
        add(InsnNode(Opcodes.POP))
        add(
            MethodInsnNode(
                Opcodes.INVOKESTATIC,
                "com/didichuxing/doraemonkit/aop/WebViewHook",
                "inject",
                "(Ljava/lang/Object;)V",
                false
            )
        )
        add(
            MethodInsnNode(
                Opcodes.INVOKESTATIC,
                "com/didichuxing/doraemonkit/aop/WebViewHook",
                "getSafeUrl",
                "(Ljava/lang/String;)Ljava/lang/String;",
                false
            )
        )
        this
    }
}

WebViewClientProxy を設定する

上記のコードにより、すべての WebView に対してloadUrl を呼び出すフックが実装され、インターセプト後の特定のロジックが通常の Java コードで記述されます。受信オブジェクトが WeView であるかどうかを確認します (挿入時にメソッド名はloadUrl であると判断されましたが、WebView である必要があるかどうかはわかりません)。次に、挿入された JS コードが確実に実行できるように、JS サポート スイッチとその他の設定を開始する必要があります。

サンプルコード:

private static void injectNormal(Object webView) {
        if (webView instanceof WebView) {
//            webView.setWebContentsDebuggingEnabled(true);
            if (!(WebViewCompat.getWebViewClient(webView) instanceof DoKitWebViewClient)) {
                WebSettings settings = webView.getSettings();
                settings.setJavaScriptEnabled(true);
                settings.setDatabaseEnabled(true);
                settings.setDomStorageEnabled(true);
                settings.setAllowUniversalAccessFromFileURLs(true);
                webView.addJavascriptInterface(new DoKitJSI(), "dokitJsi");
                webView.setWebViewClient(new DoKitWebViewClient(WebViewCompat.getWebViewClient(webView), settings.getUserAgentString()));
            }

互換性の問題の処理

アンドロイドのさまざまなバージョンと互換性があります

WebViewCompat.getWebViewClient(webView) にはバージョン互換性の問題があり、バージョン 26 以降のみをサポートします。

バージョン 21 ~ 25 と互換性があるため、WebView の setWebViewClient メソッドをフックし、WebViewClient を設定するときに WebViewClient オブジェクトを保存し、それを使用する必要があるときに WebViewClientProxy に設定する必要があります。

さまざまな WebView 実装のサポート

X5 Webview のサポート WebViewClientProxy を設定するときに、受信オブジェクトが別のブラウザ エンジンであることが検出された場合は、WebViewClientProxy を対応するプロキシ実装に置き換えます。

サンプルコード:

if (X5WebViewUtil.INSTANCE.hasImpX5WebViewLib()) {
    if (webView instanceof WebView) {
        injectNormal((WebView) webView);
    } else if (webView instanceof com.tencent.smtt.sdk.WebView) {
        injectX5((com.tencent.smtt.sdk.WebView) webView);
    }
} else {
    if (webView instanceof WebView) {
        injectNormal((WebView) webView);
    }
}

6. まとめ

HTML ファイルのダウンロードをインターセプトし、HTML の <head> タグの先頭に JS コードを含む <script> タグを挿入することで、Android 側での非侵襲的な JS コード インジェクションを実現しました。これは DoKit マルチコントロールで安定してサポートされています。 1台マルチコントロール機能はH5ページで動作します。

全体として、DoKit で使用される方法は最も効率的ではありませんし、コードの変更も最小限ではありませんが、DoKit マルチコントロール シナリオのニーズには確かに適しています。WebView に JS コードを挿入するという技術的な試みが、困っている人たちの助けになれば幸いです。

[DoKit公式交流グループ]にご注目いただき、使用上の問題があればグループ内でフィードバックをいただけると同時に、公式を通じて問題点やPRを投稿していただくことも可能です。 DoKit コードウェアハウス。

2c7013f5163a47ee0049d75b82476f97.png

 DoKit 公式コミュニケーション グループに参加するための QQ スキャン コード

Supongo que te gusta

Origin blog.csdn.net/DiDi_Tech/article/details/131447474
Recomendado
Clasificación