<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/css" href="https://imnerd.org/css/rss.css"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>前端 WebView 指南 on 怡红院落</title><link>https://imnerd.org/series/%E5%89%8D%E7%AB%AF-webview-%E6%8C%87%E5%8D%97.html</link><description>Recent content in 前端 WebView 指南 on 怡红院落</description><language>zh-CN</language><copyright>© 2021</copyright><lastBuildDate>Sun, 14 Jan 2018 20:09:00 +0800</lastBuildDate><atom:link href="https://imnerd.org/series/%E5%89%8D%E7%AB%AF-webview-%E6%8C%87%E5%8D%97/index.xml" rel="self" type="application/rss+xml"/><item><title>前端 WebView 指南之 iOS 调试篇</title><link>https://imnerd.org/ios-webview-debug.html</link><pubDate>2018-08-14</pubDate><guid>https://imnerd.org/ios-webview-debug.html</guid><description>Web开发过程中离不开调试，WebView 页面开发更是如此。本篇会和大家讲述在 iOS WebView 中一般是如何调试的。其实除去系统差异带来的区别之外，两者调试的原理上其实都差不多。
抓包 抓包即我们查看下 WebView 中的所有网络请求，在很多无法获取到机器的时候非常有用。通常页面行为不正常往往是接口请求的参数不正确，或者是接口返回的数据有异常。甚至于页面行为存在打点，根据打点请求来判断行为是否正常。通过这种“望闻问切”的方式，有些问题能够浮出水面。市面上抓包软件很多，Windows 上大家一般都是用 Fiddler, Mac 上则使用 Charles，还有其它的这里就不一一列举了。不过软件很多原理确实一样，简单的说来则是使用 HTTP 代理将所有的请求转发到软件记录本次 HTTP 请求的相关数据。
首先我们需要保证手机和电脑在同一个局域网中，软件中开启 Proxy 配置，这里以 Charles 为例，如图勾选&amp;quot;Enable transparent HTTP Proxying&amp;quot;即可。然后手机 WiFi 上配置 HTTP 代理 IP 为你的电脑 IP，端口为刚才软件中配置的端口（默认8888）。配置完后如无问题就可以在软件中看到请求流了。我们可以类似于 Chrome 开发者工具中的 Network 一样详细的查看请求的请求报文和响应报文。
由于代理的方式本质上属于中间人劫持，所以这里还有一个需要注意的是如果是 HTTPS 的网站抓包的话需要在手机端安装抓包软件的 SSL 证书。以 Charles 为例，连接上 Charles 后访问 http://chls.pro 即可获取 SSL 证书并进入安装步骤。证书安装 OK 之后我们就可以正常的对 HTTPS 的请求进行抓包了。
调试 当抓包的数据无法定位问题的时候，我们就需要使用更强力的 debug 方法了。本方法的原理是通过一定的方法获取到类似 Chrome 开发者工具的能力，可以查看并修改所有日志的输出、DOM 的结构与样式等功能。根据环境和客户端情况，实现的方法有很多种，下面我来一一说明。
Safari 如果你手机上安装的是 DEBUG 版应用，那么推荐使用 Safari 来调试，它能为 WebView 带来原生的开发者工具，可以方便的对代码进行断点调试。该方法需要满足一下三个条件才能使用：
【Safari】-&amp;gt;【偏好设置】-&amp;gt;【高级】-&amp;gt;【在菜单栏中显示“开发”菜单】勾选 【设置】-&amp;gt;【Safari】-&amp;gt;【高级】-&amp;gt;【Web检查器】打开 最重要的是 App 必须开启 DEBUG 模式 由于iOS有签名校验机制，真机正式包不允许Safari Debug，所以安装在真机上的包必须是测试签名打的包。需要联系客户端将我们 iOS 设备的 ID 写入到可信任设备列表中，然后使用 iTunes 安装客户端提供的测试包即可。
当满足以上要求后，就可以在 【Safari】-&amp;gt; 【开发】中看到自己的设备以及 WebView 中网页，点击后即可开启对应页面的 Inspector，可以用来进行断点调试。
图片来自使用 Safari 对 WebView 进行调试
weinre Safari 调试虽然强大，但是要求也比较多，往往很多时候无法满足，这个时候可以考虑使用 weinre。weinre 通过在页面中插入一段脚本，将页面的所有行为发送到服务上。首先我们需要安装并启动服务：
npm install -g weinre weinre --httpPort 8000 图片来自用Weinre远程调试移动网页
访问 http://localhost:8000 按照页面提示将 debug 脚本插入到页面中。访问页面后就会发现 winere 页面中出现了对应的请求记录，点击该记录即可跳到如下页面。可以看到这个就是一个网页版的开发者工具，可以方便的查看网络请求，控制台执行代码以及样式修改等。不过受限于原理，断点调试功能无法提供。
图片来自用Weinre远程调试移动网页
eruda 如果启动服务不方便的话，也可以采用 eruda 等方式。类似于 weinre 方法，该方法也是在页面内插入 JS 脚本将所有的数据输出到页面内的一个迷你版开发者工具中。目前市面上类似的工具非常多，不过方法都差不多，这里以 eruda 为例。页面插入脚本后点击页面中的工具按钮即可在当前页弹出 mini DevTool，可以看到所有的请求和日志等。
总结 iOS 的调试方法不是很多，常见的大概就是这些。如果 Safari 可以使用，这无疑是最优解，不过其它几种也不是没有使用场景，甚至于说出现条件无法满足只能一步一步 alert 的调试，大家针对自己的需求选择对应的调试方式即可。</description></item><item><title>前端 WebView 指南之调试篇</title><link>https://imnerd.org/webview-debug.html</link><pubDate>2018-07-14</pubDate><guid>https://imnerd.org/webview-debug.html</guid><description>WebView 是一个客户端浏览器控件，可以实现加载并渲染网页的逻辑。但是这个控件并不能完全同等于浏览器，而且我们页面的一些行为会依赖客户端的交互所以我们需要在 WebView 环境中进行调试。下面我就来说一说简单的 WebView 调试方法。
抓包 抓包即我们查看下 WebView 中的所有网络请求，在很多无法获取到机器的时候非常有用。通常页面行为不正常往往是接口请求的参数不正确，或者是接口返回的数据有异常。甚至于页面行为存在打点，根据打点请求来判断行为是否正常。通过这种“望闻问切”的方式，有些问题能够浮出水面。市面上抓包软件很多，Windows 上大家一般都是用 Fiddler, Mac 上则使用 Charles，还有其它的这里就不一一列举了。不过软件很多原理确实一样，简单的说来则是使用 HTTP 代理将所有的请求转发到软件记录本次 HTTP 请求的相关数据。
首先我们需要保证手机和电脑在同一个局域网中，软件中开启 Proxy 配置，这里以 Charles 为例，如图勾选&amp;quot;Enable transparent HTTP Proxying&amp;quot;即可。然后手机 WiFi 上配置 HTTP 代理 IP 为你的电脑 IP，端口为刚才软件中配置的端口（默认8888）。配置完后如无问题就可以在软件中看到请求流了。我们可以类似于 Chrome 开发者工具中的 Network 一样详细的查看请求的请求报文和响应报文。
由于代理的方式本质上属于中间人劫持，所以这里还有一个需要注意的是如果是 HTTPS 的网站抓包的话需要在手机端安装抓包软件的 SSL 证书。以 Charles 为例，连接上 Charles 后访问 http://chls.pro 即可获取 SSL 证书。iOS 会自动进入安装步骤，大部分 Android 机需要进入设置内的安全选项中从SD卡上安装证书。证书安装 OK 之后我们就可以正常的对 HTTPS 的请求进行抓包了。
调试 当抓包的数据无法定位问题的时候，我们就需要使用更强力的 debug 方法了。本方法的原理是通过一定的方法获取到类似 Chrome 开发者工具的能力，可以查看并修改所有日志的输出、DOM 的结构与样式等功能。根据环境和客户端情况，实现的方法有很多种，下面我来一一说明。
Android Chrome 如果你需要调试的 Android 手机版本 &amp;gt;= 4.4，则推荐使用 chrome://inspect 的方式进行调试，它能为 WebView 带来原生的开发者工具，可以方便的对代码进行断点调试。该方法需要满足一下三个条件才能使用：
Android 4.4+ 手机上开启允许 USB 连接设备进行调试 客户端开启 WebView debug 模式 //开启 webview 的 debug 模式 webview.setWebContentsDebuggingEnabled(true); 当满足以上要求之后，访问 chrome://inspect，页面将显示您的设备上已启用调试的 WebView 列表。要开始调试，请点击您想要调试的 WebView 下方的 inspect。像使用远程浏览器标签一样使用开发者工具。
图片来自远程调试 Webview
本方法需要注意的是有同学可能第一次访问的时候会白页，这是因为开发者工具本质上是一个网页，会加载 Google 相关的资源，这些资源被大陆屏蔽，需要翻墙解决。
iOS Safari 如果你手机上安装的是 DEBUG 版应用，那么推荐使用 Safari 来调试，它能为 WebView 带来原生的开发者工具，可以方便的对代码进行断点调试。该方法需要满足一下三个条件才能使用：
Mac: Safari -&amp;gt; 偏好设置 -&amp;gt; 高级 -&amp;gt; 在菜单栏中显示“开发”菜单勾选 iOS: 设置 -&amp;gt; Safari -&amp;gt; 高级 -&amp;gt; Web检查器打开 最重要的是 App 必须开启 DEBUG 模式 由于iOS有签名校验机制，真机正式包不允许Safari Debug，所以安装在真机上的包必须是测试签名打的包。需要联系客户端将我们 iOS 设备的 ID 写入到可信任设备列表中，然后使用 iTunes 安装客户端提供的测试包即可。当满足以上要求后，就可以在 Safari -&amp;gt; 开发中看到自己的设备以及 WebView 中网页，点击后即可开启对应页面的 Inspector，可以用来进行断点调试。
图片来自使用 Safari 对 WebView 进行调试
weinre 当系统版本或者未开启 debug 模式导致上面的方法不可用时，可以考虑使用 weinre。weinre 通过在页面中插入一段脚本，将页面的所有行为发送到服务上。首先我们需要安装并启动服务：
npm install -g weinre weinre --httpPort 8000 图片来自用Weinre远程调试移动网页
访问 http://localhost:8000 按照页面提示将 debug 脚本插入到页面中。访问页面后就会发现 winere 页面中出现了对应的请求记录，点击该记录即可跳到如下页面。可以看到这个就是一个网页版的开发者工具，可以方便的查看网络请求，控制台执行代码以及样式修改等。不过受限于原理，断点调试功能无法提供。
图片来自用Weinre远程调试移动网页
eruda 如果启动服务不方便的话，也可以采用 eruda 等方式。类似于 weinre 方法，该方法也是在页面内插入 JS 脚本将所有的数据输出到页面内的一个迷你版开发者工具中。目前市面上类似的工具非常多，不过方法都差不多，这里以 eruda 为例。页面插入脚本后点击页面中的工具按钮即可在当前页弹出 mini DevTool，可以看到所有的请求和日志等。
微信 WebView 调试 由于微信内查看网页的需求特别大，所以这里将微信 WebView 的调试方法单列了出来。正常来说你可以使用微信开发者工具来在电脑端进行网页与微信的调试。当这种情况无法满足，你需要在真机上排查问题时，你需要使用腾讯 x5开发的 TBS Studio 进行调试。它本质上和 chrome://inspect 方法类似，只是它为线上的微信包提供了 debug 模式，并将操作简单化。具体的使用方法可以参考官方文档：https://x5.tencent.com/tbs/guide/debug/season1.html
总结 目前市面上常见的 WebView 调试的方法大概就这些，还有一些是基于以上方法的集成和进化。如果 chrome://inspect 和 Safari 方式可以使用，这无疑是最优解，不过其它几种也不是没有使用场景，大家针对自己的需求选择对应的调试方式即可。
另外有时候用户的环境我们以上所有方法都没办法使用，而问题又不太好排查的时候，我们可以考虑记录用户交互后的页面 HTML 并发送给服务端根据 HTML 页面的情况来调试。因为用户能感知到的不正常都会在 HTML 中表现出来，通过记录用户也可以获取到用户的交互逻辑，后续通过回放用户行为方便我们排查问题。
扩展阅读：
《远程调试 WebView》
《weinre入门手册》
《移动端开发调试》</description></item><item><title>前端 WebView 指南之 iOS 交互篇</title><link>https://imnerd.org/ios-webview-and-js.html</link><pubDate>2018-04-13</pubDate><guid>https://imnerd.org/ios-webview-and-js.html</guid><description>前文我们介绍了 Android 的 WebView 交互方式，iOS 从原理上来说和 Android 还是非常类似的。在 iOS 中 WebView 需要分为UIWebView 和 iOS8 中新增的 WKWebView 两种类型。其中 WKWebView 相较于 UIWebView 优势在于能够直接使用系统 Safari 渲染引擎去渲染页面，支持更多的 HTML5 特性，渲染性能也会更好点。由于对 iOS 开发了解不太多，以下的代码大多是网络整理，没有 swift 的实现，如果有任何错误还请及时联系。
客户端调用 JS 两个 WebView 类型提供了不同的调用方式，但是基本上可以归类成以下两种：
evaluateScript 在 UIWebView 中，iOS7+ 提供了 JavascriptCore 让我们能够直接在 WebView 中获取到 JSContext，也就是当前执行环境的 JS 上下文。在这里我们就可以获取到对应的 JS 方法并执行，是非常高效的执行方式。同时这种方式的好处是能够拿到 JS 执行的结果，并转换成对应的 JS 类型。定义好 jsContext 之后就可以调用 evaluateScript 方法来执行 JS 了。
- (void)webViewDidFinishLoad:(UIWebView *)webView { JSContext *jsContext = [self.webView valueForKeyPath:@&amp;#34;documentView.webView.mainFrame.javaScriptContext&amp;#34;]; //设置JS执行报错捕获 [self.jsContext setExceptionHandler:^(JSContext *context, JSValue *exception){ NSLog(@&amp;#34;%@&amp;#34;, exception); }]; JSValue *value = [self.jsContext evaluateScript:@&amp;#34;document.title&amp;#34;]; self.navigationItem.title = value.toString; } Objective-C 数据类型 对应 JavaScript 数据类型 nil undefined NSNull null NSString string NSNumber number, boolean NSDictionary Object object NSArray Array object NSDate Date object NSBlock Function object id Wrapper object Class Constructor object 不过在 WKWebView 中没办法获取到 JSContext，不过也还是提供了 evaluateScript 方法，调用方式比起 JavascriptCore 更加简单。同时将错误捕获放置到了执行的异步回调中，对个性化错误处理比较方便。
[self.webView evaluateJavaScript:@&amp;#34;document.title&amp;#34; completionHandler:^(id _Nullable title, NSError * _Nullable error) { NSLog(@&amp;#34;Hello, %@&amp;#34;, title); }]; stringByEvaluatingJavaScriptFromString 除了 evaluateScript，两个 WebView 还提供了另外一种调用方式，那就是 stringByEvaluatingJavaScriptFromString。同样是执行一段 JS 字符串，它的优势是两者都兼容，缺点是返回值类型无法转换，只能是字符串，而且无法捕获错误。
self.navigationItem.title = [webView stringByEvaluatingJavaScriptFromString:@&amp;#34;document.title&amp;#34;]; JS 调用客户端 JS 调用 iOS 客户端的方法其实和 Android 的非常的类似，JavascriptCore 对应的是 addJavascriptInterface()，而劫持执行的方法都是通用的。
JavascriptCore 不得不说 JavascriptCore 十分强大，获取到 JSContext 上下文之后既可以读取 JS 方法，同时也可以对其写入方法以供 JS 调用。
- (void)webViewDidFinishLoad:(UIWebView *)webView { self.jsContext = [self.webView valueForKeyPath:@&amp;#34;documentView.webView.mainFrame.javaScriptContext&amp;#34;]; self.jsContext[@&amp;#34;hello&amp;#34;] = ^() { NSLog(@&amp;#34;Hello World&amp;#34;); }; } 这样加载的页面中就可以直接执行 hello() 方法来执行客户端方法了。
WKScriptMessageHandler 虽然在 WKWebView 中不支持获取 JavascriptCore，但是其提供了一套 Message Handler 协议的方式来进行客户端与 JS 的通信，和 JavascriptCore 有一些区别。
//定义 Message Handler 处理方法 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@&amp;#34;hello&amp;#34;]) { NSLog(@&amp;#34;Hello World&amp;#34;); } } WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.userContentController = [[WKUserContentController alloc] init]; //声明 hello message handler 协议 [config.userContentController addScriptMessageHandler:self name:@&amp;#34;hello&amp;#34;]; self.webview = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; self.webview.UIDelegate = self; [self.view addSubview:self.myWebView] 注册完 Message Handler 之后，JS 中会存在 window.webkit.messageHandlers 对象，我们可以如下直接调用客户端方法了。
window.webkit.messageHandlers.hello.postMessage(); URL劫持 同 Android 一样，我们也可以使用客户端劫持 URL 跳转的方式来进行 JS 与客户端的通信。URL劫持主要是使用 shouldStartLoadWithRequest() 进行 WebView URL 劫持。在该回调中我们能够获取到前端提供的 URL 地址。我们通过构造约定协议的 URL 地址提供给客户端识别，识别成功后执行对应的方法即可。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding ]; if ([requestString isEqualToString:@&amp;#34;sdk:hello&amp;#34;]) { NSLog(@&amp;#34;hello world&amp;#34;); return NO; } return YES; 方法劫持 在 WKWebView 中，JS 的 alert() 等弹窗行为方法是无法直接触发的，它们会触发客户端的方法，客户端需要手动实现这些方法。在这些方法中客户端可以获取到 JS 传入的参数，然后做相应的处理。目前前端主要有以下三种方法会触发对应的回调方法，对应关系如下：
JS方法 触发的客户端方法 alert runJavaScriptAlertPanelWithMessage prompt runJavaScriptTextInputPanelWithPrompt confirm runJavaScriptConfirmPanelWithMessage 将这三个方法列在一块是因为这几个方法的本质上都是差不多，定义好对应的回调方法即可。客户端具体的配置如下：
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { if ([message isEqualToString:@&amp;#34;sdk:hello&amp;#34;]) { NSLog(@&amp;#34;hello world&amp;#34;); return NO; } UIAlertController *alert = [UIAlertController alertControllerWithTitle:@&amp;#34;alert&amp;#34; message:@&amp;#34;JS调用alert&amp;#34; preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@&amp;#34;确定&amp;#34; style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }]]; [self presentViewController:alert animated:YES completion:NULL]; } 另外两种方法都差不多的写法，这里就不一一列举了。在实际的使用过程中我们只要约定好一种调用协议即可。
总结 本文讲述了 JS 调用客户端的方法，以及客户端调用前端的方法。JavascriptCore 和 Message Handler 方法都提供了回去执行结果的方法，而 URL 劫持则需要在 JS 调用的时候需要传入一个回调方法名，然后客户端直接执行回调方法。这样就完成了一个完成的信息交流的过程。
window.hello = function(text) { console.log(text); }; location.href = &amp;#39;$hello:{&amp;#34;callback&amp;#34;: &amp;#34;hello&amp;#34;}&amp;#39;; //以 stringByEvaluatingJavaScriptFromString 为例 [webView stringByEvaluatingJavaScriptFromString:@&amp;#34;hello(&amp;#39;hello world&amp;#39;)&amp;#34;]; 有人将通信机制进行了封装，形成一套完善的 WebviewJSBridge 方案，提供了客户端调前端，前端调用客户端的系统解决方案。例如 marcuswestin/WebViewJavascriptBridge 项目，其实它在底层是使用了 URL 劫持的方法与 JS 进行交互。使用 URL 劫持的方式主要是适用范围广，同时还能兼容 Android 端。
参考资料：
《js(javascript)与ios(Objective-C)相互通信交互》
《UIWebView和WKWebView的使用及js交互》
《iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够》
《iOS中JavaScript 与OC交互》
《iOS开发-JavaScriptCore、UIWebView及WKWebView交互的那些事》
《WKWebView使用简介》
《IOS WKWebview 和JS交互》</description></item><item><title>前端 WebView 指南之 Android 交互篇</title><link>https://imnerd.org/android-webview-and-js.html</link><pubDate>2018-12-13</pubDate><guid>https://imnerd.org/android-webview-and-js.html</guid><description>WebView 是移动端应用中的一个控件，提供了类似浏览器可以在 App 中加载网页的功能。现在市面上很多应用都会使用这种方式内嵌一些 h5 页面用来实现产品功能。使用这种方式带来的好处就是支持快速迭代更新，并且页面的功能是全网升级。当然目前 RN 和 Codorva 给我们带来的热更新方案也是可以的，只是目前 Apple 的态度很拒绝，这里我们略过不表。在 WebView 中的网页势必存在和客户端进行交互的动作，进行数据的共享。下面我们就来说说 Android WebView 中 JS 和 Native 的交互方式。
客户端调用 JS loadUrl() 我们明白 WebView 其实就是在加载网页，所以客户端可以直接访问 javascript:console.log('hello') 这样的伪 URL 即可实现在页面注入需要执行的 JS 代码。调用方法如下：
WebView webview = (WebView) findViewById(R.id.webView); webview.loadUrl(&amp;#34;javascript:console.log(&amp;#39;hello&amp;#39;)&amp;#34;); 这样我们就实现了调用 JS 的目的了。loadUrl() 的方案从另外一个角度来看可以算是 hack 方案了，对客户端来说，他们的 JS 交互本质上其实就是一个拼接 JS 字符串的过程。
evaluateJavascript() 刚才我们也说了 loadUrl() 不是 Android 的正经解决方法。好在官方也想到了这点，在 Android 4.4+ 之后，官方给提供了原生的方法支持调用，那就是 evaluateJavascript()。这个方法最大的好处就是能够直接在一次执行的时候获取到 JS 返回的结果。如果是使用 loadUrl() 的方式的话，执行完后对客户端来说这句话就结束了，如果想要拿到返回的结果的话另外需要 JS 调用客户端的方法返回。
WebView webview = (WebView) findViewById(R.id.webView); webview.evaluateJavascript（&amp;#34;javascript:Date.now()&amp;#34;, new ValueCallback&amp;lt;String&amp;gt;() { @Override public void onReceiveValue(String value) { System.out.println(value); //1515827651551 } }); 可以看到调用方法和 loadUrl() 非常类似，区别是增加了一个 callback 方法可以获取到 JS 返回的值。该方法无疑比较优秀，不过对兼容性有要求，目前市面上用还是使用前一种方法的比较多。
JS 调用客户端 相比较客户端调用 JS 的方法，JS 调用客户端的方法就比较多了，简单归类一下其实可以分为注入映射和方法劫持两种。注入映射主要是使用官方提供的 addJavascriptInterface() 方法将 Java 对象和 JS 对象进行映射。而方法劫持则是利用 JS 的一些系统方法调用会触发 Java 的事件回调，然后在回调中进行事件劫持，从而执行客户端方法。下面我们来具体看看。
addJavascriptInterface addJavascriptInterface() 方法的使用非常简单，定义好被调用的方法对象后直接配置映射关系即可。
//定义好 Java 接口对象 public class SDK extends Object { @JavascriptInterface public void hello(String msg) { System.out.println(&amp;#34;Hello World&amp;#34;); } } //Webview 中调用 WebView webview = (WebView) findViewById(R.id.webview); webview.addJavascriptInterface(new SDK(), &amp;#39;sdk&amp;#39;); webview.loadUrl(&amp;#39;https://imnerd.org&amp;#39;); //注入后加载页面 这样加载的页面中就可以直接执行 sdk.hello() 方法来执行客户端方法了。不过这种官方推荐的方法在 4.2- 的系统上存在远程执行安全漏洞，对 4.2 以下系统版本有要求的应用需要谨慎使用。目前来看 4.2 还是需要保持支持的。
URL劫持 URL劫持主要是使用 shouldOverrideUrlLoading() 进行 WebView URL 劫持。从方法名可以看出，它是 WebView 拦截 URL 的一种回调，当 WebView 发生 URL 跳转的时候会触发该回调。在该回调中我们能够获取到前端提供的 URL 地址。我们通过构造约定协议的 URL 地址提供给客户端识别，识别成功后执行对应的方法即可。
WebView webview = (WebView) findViewById(R.id.webview); webview.loadUrl(&amp;#39;https://imnerd.org&amp;#39;); webview.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if(url.equals(&amp;#39;sdk:hello&amp;#39;)) { System.out.println(&amp;#39;hello world&amp;#39;); return true; } return super.shouldOverrideUrlLoading(view, url); } }); 方法劫持 同 URL 劫持类似，方法劫持主要是利用 JS 的一些方法执行时会触发 Android 客户端中的一些回调，通过对前端参数进行识别来执行对应的客户端代码。目前前端主要有以下四种方法会触发对应的回调方法，对应关系如下：
JS方法 客户端回调 alert onJsAlert prompt onJsPrompt confirm onJsConfirm console.log onConsoleMessage 将这四个方法列在一块是因为这几个方法的本质上都是差不多，定义好对应的回调方法即可。客户端具体的配置如下：
//定义好劫持回调类 private class hijackWebChromeClient extends WebChromeClient { public boolean hijack(String text) { if(text.equals(&amp;#39;sdk:hello&amp;#39;)) { System.out.println(&amp;#39;hello world&amp;#39;); return true; } return false; } @Override public boolean onJsPrompt(WebView view, String message, String defaultValue, JSPromptResult result) { if(this.hijack(message)) { return true; } return super.onJsPrompt(view, url, message, defaultValue, result); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { if(this.hijack(message)) { return true; } return super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { if(this.hijack(message)) { return true; } return super.onJsConfirm(view, url, message, result); } @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { String message = consoleMessage.message(); if(this.hijack(message)) { return true; } return super.onConsoleMessage(consoleMessage); } @Override public void onConsoleMessage(String message, int lineNumber, String sourceID) { if(this.hijack(message)) { return true; } super.onConsoleMessage(message, lineNumber, sourceID); } } //注入劫持回调类 WebView webview = (WebView) findViewById(R.id.webview); webview.loadUrl(&amp;#39;https://imnerd.org&amp;#39;); webview.setWebChromeClient(new hijackChromeClient); 这里为了方便展示，将所有回调的方法都写全了，实际上在实际的使用过程中一般都是约定好一种调用方式即可。另外 console.log 对应的回调写了两种，三参数的是老版本方法，在新API中已经被废弃，推荐使用 ConsoleMessage 对象传参方式。
总结 以上讲述了 JS 调用客户端的方法，以及客户端调用前端的方法。除了这两种单向调用的方式之外，往往比较多的是 JS 调用客户端方法，客户端再调用 JS 返回结果的双向调用。在 JS 调用的时候需要传入一个回调方法名，然后客户端直接执行回调方法。这样就完成了一个完成的信息交流的过程。
window.hello = function(text) { console.log(text); }; console.log(&amp;#39;$hello:{&amp;#34;callback&amp;#34;: &amp;#34;hello&amp;#34;}&amp;#39;); webview.loadUrl(&amp;#39;javascript:hello(&amp;#34;hello world&amp;#34;)&amp;#39;); 这些调用方法有两点需要注意：
不管是前端调用还是客户端调用，所有的调用的结果返回都是异步的。客户端 loadUrl() 需要另外通过 JS 异步回调客户端方法告诉结果，evaluateJavascript() 也需要传如一个异步回调方法。前端调用中 addJavascriptInterface() 是无返回值的，而方法劫持中，需要等待客户端回调我们的 JS 方法才能异步获取到数据。所以我们需要对异步通信进行妥善处理。 由于 JS 和客户端无法实现内存共享，所以所有的数据必须字符串化，只能通过字符串进行交流。例如两边的复杂对象数据，需要使用类似 JSON 的格式进行字符串化，而文件/图片等二进制数据最好使用 base64 字符串化。 后记 基本上交互的基本方式就是以上几种，不过有人将通信机制进行了封装，形成一套完善的 WebviewJSBridge 方案，提供了客户端调前端，前端调用客户端的系统解决方案。例如 lzyzsd/JsBridge 项目，我们从代码中可以看到，其实它在底层是使用了 URL 劫持的方法与 JS 进行交互。虽然原理简单，不过它提供了系统方案，同时也统一了 Android 和 iOS 多端的调用方法，如果是准备从0开始实现交互的话推荐使用。
参考资料：
《Android：你要的WebView与 JS 交互方式 都在这里了》
《Android 利用WebViewJavascriptBridge 实现js和java的交互》</description></item></channel></rss>