Android 原生 WebView 与 JavaScript 交互

现在的纯原生的 APP 很少见了,几乎都是原生中嵌者网页的 hybird APP,或者直接使用 React Native 开发。原生 App 中嵌者网页原生与网页中 JavaScript 的交互几乎无法避免,毕竟在自己的应用中嵌入网页不只是想让应用当个浏览器用。
Android 原生中加载网页的控件是 WebView,因此要想原生与 JavaScript 交互也一样必须通过 WebView 来实现。

相关类和主要方法

WebView

低版本和高版本的 Android 系统中 WebView 采用不同的内核, Android 4.4 之后直接使用了 Chrome 的内核。
常用方法:

//根据 url 去加载一个网页
webView.loadUrl(String url);

//根据 HTML 数据显示出内容,其中 loadData() 方法的 data 中不能出现英文的 %,#,\,? 四个字符,否则可能会报错
webView.loadData(String data,String mimeType,String encoding);
webView.loadDataWithBaseUrl(String baseUrl,String mimeType,String encoding,String historyUrl);

// webView 处于激活状态,能正常加载和响应网页
webView.onResume();

// webView 处于暂停状态,当页面失去焦点切换到后台时调用
// 处于 pause 状态的 WebView 会停止动画和计算,但是不会停止 JavaScript 的执行
webView.onPause();

//暂停所有 WebView 的 layout、parsing、JavaScriptTimers 以降低 CPU 消耗(全局有效)
webView.pauseTimers();

// 恢复 pauseTimers 的暂停状态
webView.resumeTimers();

// 销毁一个 WebView 时要先将 WebView 和当前界面解绑再销毁
parentLayout.removeView(webView);
webView.destroy();

// 判断网页是否可以后退(可以实现监听,当网页可以后退事点击返回键和返回按钮不退出浏览界面)
webView.canGoBack();

//后退网页
webView.goBack();

// 判断网页是否可以前进
webView.canGoForward();

//前进网页
webView.goForward();

//以当前页为起点前进后退 steps 个页面(正数前进,负数后退)
webView.goBackOrForward(int steps);

WebSetting

WebSetting 是为 WebView 提供配置和管理的一个抽象类,它通过 webView.getSettings() 方法获取实例。
常用方法:

// 设置 webView 是否支持 JavaScript 的调用(应用中涉及原生与 JS 交互的必须设置为 true)
webSettings.setJavaScriptEnabled(true);

// 设置是否允许 JS 开启新窗口(function window.open())
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

// 设置 webView 是否支持插件
webSettings.setPluginsEnable(true);

// 设置编码方式

// 设置是否自适应屏幕,(一般图片和网页缩放同时使用)
webSettings.setUseWideViewPort(true); //将图片调整到适合 webView 的大小
webSettings.setLoadWithOverviewMode(true); //缩放至屏幕的大小(显示未做移动端兼容的 PC 网页)

// 设置是否支持缩放,默认为支持
webSettings.setSupportZoom(true);
// 设置内置的缩放控件
webSettings.setBuiltInZoomControls(true);
// 隐藏原生的缩放控件
webSettings.setDisplayZoomControls(true);

WebViewClient

WebViewClient 主要用来帮助 WebView 处理请求的各种状态事件。
常用方法:

// 页面开始加载的时候回调
onPageStarted(WebView view, String url, Bitmap favicon);
// 页面加载结束时回调
onPageFinished(WebView view, String url);
// 将要加载资源时回调,每次资源的加载都会调用
onLoadResource(WebView view, String url);
// 加载的页面 404 时将会回调
onReceivedError(WebView view, WebResourceRequest request, WebResourceError error);

webView 默认情况下是不会加载 https 请求的,页面将显示空白,如果加载 https 页面需要进行如下设置:

webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }    
    });

WebChromeClient

辅助 WebView 处理 JavaScript 对话框,加载进度,网页图标等。
常用方法:

// 加载进度回调
onProgressChanged(WebView view, int newProgress);
// 获取到网页 Title 回调
onReceivedTitle(WebView view, String title);
// 获取到网页图标回调
onReceivedIcon(WebView view, Bitmap icon);

WebView 的使用

通过前面的了解,WebView 及他的辅助类的一些基本方法我们是知道了,接下来就看一下一下怎么使用 WebView 以及怎样通过 WebView 让 Android 源生代码与 JavaScript 代码进行交互。

JS 调用原生方法

JS 调用源生的方法时该方法必须要加上 @JavascriptInterface 注解,我们可以定义一个类或者接口来单独存放用于 JS 调用的方法,这里我以接口为例。接口中提供一个 getUrl(String url) 方法用于提供给 JS 调用,传递一个 String 类型的值给源生方法:

/**
 * Created by PandaQ on 2017/3/22.
 * JS 接口方法定义
 * 接口中定义方法时不用加注解,使用时才加注解
 */
public interface JavaScriptFunction {
    void getUrl(String string);
}
// 此代码片段为 PandaEye 中点击加载的 html 数据中的图片跳转到新的 Activity 显示图片的功能
webView.addJavascriptInterface(new JavaScriptFunction() {
            @Override
            @JavascriptInterface // 加上注解 getUrl() 方法才能被 JS 调用
            public void getUrl(String imageUrl) {
                LogWritter.LogStr(imageUrl);
                Intent intent = new Intent();
                intent.putExtra("imageUrls", mImageUrls);
                intent.putExtra("curImageUrl", imageUrl);
                intent.setClass(ZhihuStoryInfoActivity.this, PhotoViewActivity.class);
                startActivity(intent);
            }
        }, "JavaScriptFunction");
// 在 HTML 中图片的点击事件 JS 方法中就可以执行如下代码来调用源生的接口方法
function(){
    window.JavaScriptFunction.getUrl(this.src);
}

原生调用 JS

在熊猫眼 PandaEye 中因为使用的知乎日报和网易新闻的 API 所以下发的 HTML body 数据需要我们自己加个 HTML 的壳,然后再源生加载我们需要的 JavaScrpit 代码来实现我们的功能。
加载 JS 可以直接 webView.load() 加载完整的 JS 代码也可以在自己加的 head 节点引入 JS 文件然后 webView 直接 load 其中的方法, PandaEye 中使用的是第二种方法:
在拼装 HTML 时在 head 中引入放在 asset 文件夹中的 js 文件

enter description here
enter description here

imageClick.js 中的内容如下:

function initClick()
{
    var objects = document.getElementsByTagName("img");
    for(var i=0;i<objects.length;i++)
    {
        objects[i].onclick= function (){
            window.JavaScriptFunction.getUrl(this.src);
        }
    }
}

然后在 WebChromeClient 当加载进度达到 100% 后去调用 JS 文件中的 initClick() 方法,为每一张图片设置点击事件:

    /**
     * 为所有的图片添加点击事件
     *
     * @param webView 对应的 WebView
     */
    private void addImageClickListener(WebView webView) {
        webView.loadUrl("javascript:(initClick())()");
    }

直接加载 JS 把 initClick() 用 JS 代码替换掉即可。

利用 Chrome 调试 WebView

WebView 中 JavaScript 代码的调试直接使用 AndroidStudio 是没办法的,那么我们怎样调试 HTML 页面呢?答案是用 Chrome 浏览器来调试:

  • USB 选项是打开的(AS 能调试应用就行)
  • Chrome 浏览器打开地址:chrome://inspect 或者 about:inspect
    打开待调试的 WebView 然后可以看到如下界面:
InspectPage
InspectPage

点击对应页面的 inspect 就可以进入调试页面:

DebugPage
DebugPage

接下里就可以像调试前端页面一样调试 WebView 中的内容了。

最后

详细使用代码请移步 PandaEye 查看

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容