addJavaScriptInterface()メソッドを使用してWebViewでJavaオブジェクトをバインドする

4075 ワード

1.説明
WebViewでは、開発者がJavaScript APIネーミングスペースを拡張し、Javaによってそれぞれのコンポーネントを定義してJavaScript環境で使用できるようにします.プラットフォームにアクセスしたいのにJavaScriptのメソッドが使用できない場合や、JavaScriptでJavaで作成されたコンポーネントを使用したい場合、このテクノロジーWebViewのこの方法は次のように目的を達成できます.
    JavaScriptInterface JavaScriptInterface = new JavaScriptInterface(this);
    myWebView = new MyWebView(this);
    myWebView.addJavaScriptInterface(JavaScriptInterface, "HybridNote");

この例では、JavaScriptInterfaceはWebViewのJavaScript環境にバインドされ、HybridNoteオブジェクト(aka namespace)を介してアクセスすることができる.このバインディングオブジェクトのすべての共有またはいくつかの特定のメソッドは、JavaScriptコードで参照にアクセスできます.このオブジェクトが前に指定した関数を使用してWebViewに追加されると、このオブジェクトはWebView内のページのロードが完了したか、ロードされたページが存在する場合にのみJavaScriptで参照できます.
addJavaScriptInterface()はHybridAppを設計する際に強力な方法ですが、この方法を使用すると大きなセキュリティ上の危険性があります.これは、同源ルール(SOP)がこの方法に適用されないためであり、サードパーティJavaScriptライブラリまたは見知らぬドメイン名からのiframeがJavaレイヤでこれらの暴露された方法にアクセスする可能性があるためである.したがって、攻撃者は、XSSホールを介してオリジナルコードを実行したり、ウイルスコードをアプリケーションに注入したりすることができる.
JavaScriptレイヤで露出されたJavaオブジェクトのすべての公開メソッドは、AndroidバージョンがJerryBean MRI(API Level 17)以下の場合にアクセスできます.Google API 17(4.2)以上では,暴露された関数は@JavaScriptInterface注釈によって方法の暴露を防止しなければならない.
2.使用
WebViewでjavaオブジェクトを呼び出す方法については、インスタンスを使用して説明します.次の例では、WebViewのページのタイトルをパラメータとして取得してJavaオブジェクトを呼び出してTextViewにタイトルを表示する方法を説明します.WebChromeClientのonReceivedTitleを書き換えることでページのタイトルを取得して表示することができますが、この方法ではページがロールバックされるときに必ずしも呼び出されるわけではありません.ロールバック時に表示されるのは前のページのタイトルであるため,アクセスしたURLやページタイトルをスタックで記録できるとしても,この方法には一定の限界がある.
addJavaScriptInterface()メソッドを使用すると、ページタイトルがページコンテンツに保存されているため、Javaオブジェクトのメソッドをパラメータとして呼び出すことができ、そのメソッドの機能はタイトルを表示することであり、具体的なコードは以下の通りである.
public class MainActivity extends AppCompatActivity {
    
    private WebView webview;
    private TextView tvTitle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webview = (WebView)findViewById(R.id.webView);
        tvTitle = (TextView)findViewById(R.id.tvTitle);
        
        WebSettings settings = webview.getSettings();
        settings.setJavaScriptEnabled(true);//         JavaScript 
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setBuiltInZoomControls(true);//         
        settings.setSupportZoom(true);//       
        settings.setSupportMultipleWindows(true);

        webview.addJavascriptInterface(new GetTitle(), "getTitle"); //  webview    Java  
        webview.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                //    JavaScript,        Java                 
                view.loadUrl("javascript:window.getTitle.onGetTitle("
                        + "document.getElementsByTagName('title')[0].innerHTML" + ");");
                super.onPageFinished(view, url);
            }
        });

        webview.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onReceivedTitle(WebView view, String title) {
                tvTitle.setText(title);
            }
        });

        webview.loadUrl("https://www.baidu.com/");
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK) {
            if(webview.canGoBack()) {
                webview.goBack();
            } else {
                finish();
            }
            return true;

        }
        return super.onKeyDown(keyCode, event);
    }

    //  JavaScript Java 
    class GetTitle {
        @JavascriptInterface
        public void onGetTitle(String title) {
           tvTitle.setText(title);     //    
        }
    }

    @Override
    protected void onDestroy() {
        webview.destroy();
        super.onDestroy();

    }
}