Android中js呼叫java本地方法的三種方式

風靈使發表於2018-10-30

MainActivity.java

package com.fedming.webtonativedemo;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.util.HashMap;

/**
 * <pre>
 *     author : fdm
 *     time   : 2018/03/08
 *     desc   : WebView與本地方法互動的三種方法Demo
 *     version: 1.0
 * </pre>
 */

public class MainActivity extends Activity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = (WebView) findViewById(R.id.webView);
        WebSettings webSettings = webView.getSettings();
        webSettings.setAllowFileAccess(false);
        webSettings.setSupportZoom(false);
        webSettings.setUseWideViewPort(true);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setLoadsImagesAutomatically(true);
        webSettings.setDefaultTextEncodingName("UTF-8");
        webView.setWebChromeClient(new WebChromeClient());

        /**
         * 第一種
         * 原理:直接構造本地化物件,對映到js頁面中訪問,底層原理藉助了V8引擎(Android4.2 以下系統有WebView漏洞)
         */
        webView.addJavascriptInterface(new JSInterface(), "MyObj");

        /**
         * 第二種
         * 回撥方法中(shouldOverrideUrlLoading)攔截請求url,分析url格式以及自定義協議、引數名稱可得到具體引數
         * tips:需要再次呼叫頁面js方法獲取返回值
         */
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if (url.startsWith("http:") || url.startsWith("https:")) {
                    return false;
                }
                //協議url:"js://demo?arg=111"
                try {
                    String protocol = Utils.getUrlScheme(url);
                    if ("js".equals(protocol)) {
                        HashMap<String, String> map = Utils.getUrlParams(url);
                        String arg = map.get("arg");
                        String res = getLocalString(arg);
                        //再次呼叫web中js方法,將引數傳回web去
                        webView.loadUrl("javascript:click_result(" + res + ")");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }

        });

        /**
         * 第三種
         * 藉助WebChromeClient的回撥方法(共三個),攔截JS中的三個方法:alert,confirm,prompt,解析引數,得到指定格式資料
         * tips:需要頁面和本地解析格式做一個約束
         */
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                return super.onJsAlert(view, url, message, result);
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
                return super.onJsConfirm(view, url, message, result);
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                if (url.startsWith("http:") || url.startsWith("https:")) {
                    return false;
                }
                //協議url:"js://demo?arg=111"
                try {
                    String protocol = Utils.getUrlScheme(message);
                    if ("js".equals(protocol)) {
                        HashMap<String, String> map = Utils.getUrlParams(message);
                        String arg = map.get("arg");
                        String res = getLocalString(arg);
                        result.confirm(res);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
        });

        //載入asset中的網頁
        webView.loadUrl("file:///android_asset/js_demo.html");

    }

    /**
     * 本地化JS物件,供web呼叫
     * sdk17版本以上需要加上註解
     */
    class JSInterface {
        @JavascriptInterface
        public String getPwd(String pwd) {
            //執行本地方法,返回結果到web
            return getLocalString(pwd);
        }
    }

    /**
     * 本地方法
     *
     * @param txt txt
     * @return txt
     */
    public String getLocalString(String txt) {
        return txt;
    }

}

Utils.java

package com.fedming.webtonativedemo;

import java.util.HashMap;

/**
 * <pre>
 *     author : fdm
 *     time   : 2018/03/08
 *     desc   : Utils
 *     version: 1.0
 * </pre>
 */

public class Utils {

    public Utils() {
    }

    /**
     * 獲取url中的協議
     *
     * @param url url
     * @return protocol
     */

    public static String getUrlScheme(String url) {
        int index = url.indexOf(":");
        return url.substring(0, index);
    }

    /**
     * 獲取url中的引數
     *
     * @param url url
     * @return args
     */
    public static HashMap<String, String> getUrlParams(String url) {
        int index = url.indexOf("?");
        String argStr = url.substring(index + 1);
        String[] argAry = argStr.split("&");
        HashMap<String, String> argMap = new HashMap<String, String>(argAry.length);
        for (String arg : argAry) {
            System.out.println("arg:" + arg);
            String[] argAryT = arg.split("=");
            argMap.put(argAryT[0], argAryT[1]);
        }
        return argMap;
    }

}

main資料夾下 assets資料夾

js_demo.html

<!DOCTYPE html>
<html>
<head>
    <title>WebToNativeDemo</title>
    <script>
	    function click_one(){
	    	var result = window.MyObj.getPwd("111");
	        alert("第一個按鈕結果:"+result);
	    }

	    function click_two(){
	    	document.location = "js://demo?arg=222";
	    }

       function click_result(result){
	    	alert("第二個按鈕結果:"+result);
	    }

	    function click_three(){
	    	var result=prompt("js://demo?arg=333","");
	    	alert("第三個按鈕結果:"+result);
	    }
	    
    </script>
    <style type="text/css">
    	input{
    		width: 800px;
    		height: 180px;
    		margin: 50px auto;
    		text-align:center;
    		font-size:50px;
    	}

    </style>

</head>

<body>
<p style=" margin:0 auto; text-align:center;"><input id="one" class="submit" name="submit" type="submit" onclick="click_one()"/></p>
<p style=" margin:0 auto; text-align:center;"><input id="two" class="submit" name="submit" type="submit" onclick="click_two()"/></p>
<p style=" margin:0 auto; text-align:center;"><input id="three" class="submit" name="submit" type="submit" onclick="click_three()"/></p>
</body>

</html>

layout資料夾

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/linear_layout"
        android:layout_width="match_parent"
        android:layout_height="54dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="WebToNative"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/linear_layout" />
</RelativeLayout>

app資料夾下build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion '27.0.3'
    defaultConfig {
        applicationId "com.fedming.webtonativedemo"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    testCompile 'junit:junit:4.12'
}

專案根目錄下build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
        google()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

gradle-wrapper.properties

#Tue Oct 30 12:36:36 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.fedming.webtonativedemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

執行結果如圖:

在這裡插入圖片描述

在這裡插入圖片描述

相關文章