码蛋程序员Android技术知识

Android WebView:我是怎么和 JavaScript

2016-08-28  本文已影响432人  MoTalksCn_林墨

如今React Native都红火了一年多了,现在还整WebView 和 JavaScript这些? 哈哈,事情总有个循序渐进,况且现在这个还是主流处理方案吧,大家还是有必要了解的,文章很基础,高手轻喷。

Demo动图演示

动图镇楼,演示功能点的顺序是:5-1-2-3-4

功能点动图演示功能点动图演示

怎么生成我们需要的 HTML 页面?

当我们get了WebView 和 JavaScript交互的技能时,准备跃跃欲试的时候,就会对这个被这个问题搞懵逼了,他娘的我又不是前端,要我立马写个自己想要的HTML页面臣妾真的办不到啊。

w3school 是什么?

是什么?你咋不点进去看看呢?

我们需要的 HTML 页面

按照w3school的简单教程我们就可以拼凑出我们所需要的页面了,如下图。


我们需要的 HTML 页面我们需要的 HTML 页面

我们要实现的5个功能

Android Native 端调用 HTML 中的 js 脚本

该例子中Android Native 端的 java 代码会直接调用 HTML 页面中的js 脚本,实现更改 HTML 页面 subtitle 的功能。
Android Native 端的代码为:

 mCallJsBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCallJsWithArgsBtn.setText("Native调用WebView的有参JS脚本");
            mWebView.loadUrl("javascript:changeDemoSubtitle()");
        }
    });

HTML 对应的 js 方法是:

function changeDemoSubtitle(){
document.getElementById("subtitle").innerHTML="我加了点料";

}

Android Native 端调用 HTML 中的 js 脚本并传递参数

Android Native 端的代码为:

mCallJsWithArgsBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCallJsWithArgsBtn.setText("参数为:" + showDevInfo());
            showDevInfoToH5();
        }
    });
    private String showDevInfo() {
    return "showDevInfo('手机型号:" + android.os.Build.MODEL +
            ",SDK版本:" + android.os.Build.VERSION.SDK +
            ",系统版本:" + android.os.Build.VERSION.RELEASE + "')";
}

private void showDevInfoToH5() {
    mWebView.loadUrl("javascript:" + showDevInfo());
}

HTML 对应的 js 方法是:

function showDevInfo(info){
document.getElementById("subtitle").innerHTML=info;

}

HTML 中的 javascript 脚本调用 Android Native 端的函数

HTML 对应的 js 方法是:

function disp_confirm(){
    confirm();
}

Android Native 端的代码为:

mWebView.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            Toast.makeText(MainActivity2.this, "Android_Native_Toast!", Toast.LENGTH_SHORT).show();
            }
            result.confirm();
            return true;
        }
    });

HTML 中的 javascript 脚本调用Android Native 端的函数并传递参数

HTML 对应的 js 方法是:

function disp_confirm_with_args(message){
    confirm(message);
    }

Android Native 端的代码为:

mWebView.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            Toast.makeText(MainActivity2.this, "Android_Native_Toast!接收到的参数message:" + message, Toast.LENGTH_SHORT).show();
            }
            result.confirm();
            return true;
        }
    });

HTML 中的 javascript 脚本与 Android Native端的 Java 代码完整交互流程

HTML 对应的 js 方法是:

function disp_confirm_with_args(message){
    confirm(message);
    }
    <input type="button" onclick="disp_confirm_with_args('getDevInfo')" value="点击获取用户手机信息"/>

Android Native 端的代码为:

mWebView.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            if ("getDevInfo".equals(message)) {
                showDevInfoToH5();
            } 
            }
            result.confirm();
            return true;
        }
    });
    private String showDevInfo() {
    return "showDevInfo('手机型号:" + android.os.Build.MODEL +
            ",SDK版本:" + android.os.Build.VERSION.SDK +
            ",系统版本:" + android.os.Build.VERSION.RELEASE + "')";
}

private void showDevInfoToH5() {
    mWebView.loadUrl("javascript:" + showDevInfo());
}

Android Native 端代码完整展示:

Activity 代码:

package cn.mtalks.jsbridgedemo;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity2 extends Activity {

private WebView mWebView;
private Button mCallJsBtn;
private Button mCallJsWithArgsBtn;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_2);
    initView();
    initWebView();
}

private void initView() {
    mWebView = (WebView) findViewById(R.id.webview);
    mCallJsBtn = (Button) findViewById(R.id.call_js_without_args_btn);
    mCallJsWithArgsBtn = (Button) findViewById(R.id.call_js_with_args_btn);

    mCallJsBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCallJsWithArgsBtn.setText("Native调用WebView的有参JS脚本");
            mWebView.loadUrl("javascript:changeDemoSubtitle()");
        }
    });

    mCallJsWithArgsBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCallJsWithArgsBtn.setText("参数为:" + showDevInfo());
            showDevInfoToH5();
        }
    });
}

private void initWebView() {
    //开启JavaScript功能
    mWebView.getSettings().setJavaScriptEnabled(true);
    //加载本地html
    mWebView.loadUrl("file:///android_asset/JsDemo.html");

    mWebView.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            if ("getDevInfo".equals(message)) {
                showDevInfoToH5();
            } else if (TextUtils.isEmpty(message)) {
                Toast.makeText(MainActivity2.this, "Android_Native_Toast!", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity2.this, "Android_Native_Toast!接收到的参数message:" + message, Toast.LENGTH_SHORT).show();
            }
            //回调通知H5页面用户操作已完成,可以再次点击相关按钮
            result.confirm();
            //设为true,可以消耗掉H5页面的confirm弹窗。
            return true;
        }
    });
}

private String showDevInfo() {
    return "showDevInfo('手机型号:" + android.os.Build.MODEL +
            ",SDK版本:" + android.os.Build.VERSION.SDK +
            ",系统版本:" + android.os.Build.VERSION.RELEASE + "')";
}

private void showDevInfoToH5() {
        mWebView.loadUrl("javascript:" + showDevInfo());
    }
}

XML 代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#000000"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:paddingBottom="10dp"
        android:paddingTop="10dp"
        android:text="Android_Native"
        android:textColor="#FFFFFF" />

    <Button
        android:id="@+id/call_js_without_args_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="Native调用WebView的JS脚本" />

    <Button
        android:id="@+id/call_js_with_args_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:paddingBottom="10dp"
        android:paddingTop="10dp"
        android:text="Native调用WebView的有参JS脚本" />
</LinearLayout>

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    />
</LinearLayout>

AndroidManifest 文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.mtalks.jsbridgedemo">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity2"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>
    </application>
</manifest>

友情提示

其实在JavaScript调用 Java本地方法,谷歌官方的实现方法应该是使用 JavascriptInterface 方式。但是该方式在 Android4.2版本之前存在安全漏洞,感兴趣的看这里。在Android4.2版本之后加入了@JavascriptInterface注解才得以解决,考虑到目前 Android 版本碎片化太严重,使用JavascriptInterface并不适合,这也是上篇文章没把它放入效率对比的原因。

以上就是一个完整的 WebView 与 JavaScript 的交互例子。大家可以自己动手试试上篇文章介绍的ShouldInterceptRequest方式来实现这个例子。

下篇预告

Webview 页面展示优化。除了 HTML 页面自己的优化,在 Android Native 端能为加载速度做些什么优化么?下篇文章会给你答案,比如公共 Js 方法提取放入本地,JS,CSS,图片资源预加载等等。

上一篇 下一篇

猜你喜欢

热点阅读