博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JockeyJS——优秀的WebView与JS交互开源库使用和解析
阅读量:6575 次
发布时间:2019-06-24

本文共 6189 字,大约阅读时间需要 20 分钟。

前言

在Android上,对于JS交互,往往是通过系统原生提供的@JavascriptInterface这种方式进行交互的,而本人在项目的应该也是使用这种方式。最近听朋友提到一个库——JockeyJS,封装了JS交互逻辑,通过少量的接口让开发者只需要关注Java和JS之间的方法调用。我对它避开@JavascriptInterface的实现比较感兴趣,后来发现JockeyJS有于Java和JS之间的方法调用和回调有着不错的封装,于是便有了分析JockeyJS一文。

一、JockeyJS基本使用

JockeyJS是几年前的库了,虽然是比较久的库,但放到现在仍然可用。

首先,需要在h5页面上引用项目中的jockey.js

接下来在客户端进行配置,JockeyJS主要通过on(String type, JockeyHandler ... handler)send(String type, WebView toWebView, Object withPayload, JockeyCallback complete) 两个方法来实现Java与JS之间的交互。

  • on(String type, JockeyHandler ... handler)这一接口让我们可以在Java上提供给JS需要调用的方法,类似于@JavascriptInterface的功能,type是我们提供的方法名,handler中的回调是我们运行的代码。
jockey.on("useJavaMethod", new JockeyHandler() {	@Override	protected void doPerform(Map
payload) { // do something }});复制代码
  • send(String type, WebView toWebView, Object withPayload, JockeyCallback complete)用于Java调用JS方法,type是调用的方法名,toWebView是调用的webView,withPayload是参数,会转成json传递,complete是调用成功后的回调。
jockey.send("useJsFuntion", webView, null, new JockeyCallback() {	@Override	public void call() {		// secceed to use js function	}});复制代码

二、JockeyJS原理解析

参考JockeyJS提供的demo,在JockeyJS生效前,需要进行以下设置

jockey = JockeyImpl.getDefault();jockey.configure(webView);setJockeyEvents();复制代码
  • JockeyImpl.getDefault()这里提供了对Jockey接口的默认实现,也就是对于JS交互这一核心功能的实现。
  • jockey.configure(webView)向JockeyJS传入webView,JockeyJS会对webView进行setJavaScriptEnabled(boolean)setWebViewClient(WebViewClient)的设置。
  • setJockeyEvents()即一系列的on(String type, JockeyHandler ... handler)操作,添加可供调用的Java方法。

这样的话我们主要关注JockeyImpl.getDefault()的实现。

public static Jockey getDefault() {	return new DefaultJockeyImpl();}复制代码

可见该方法返回的是DefaultJockeyImpl。跟进DefaultJockeyImpl,发现该类也是继承了JockeyImpl类的,我们先来看DefaultJockeyImpl实现。主要看send(String type, WebView toWebView, Object withPayload, JockeyCallback complete)

1. Java调用JS的实现

public void send(String type, WebView toWebView, Object withPayload, JockeyCallback complete) {	int messageId = messageCount;	if (complete != null) {		add(messageId, complete);	}	if (withPayload != null) {		withPayload = gson.toJson(withPayload);	}	String url = String.format("javascript:Jockey.trigger(\"%s\", %d, %s)",			type, messageId, withPayload);	toWebView.loadUrl(url);	++messageCount;}复制代码

该方法中有一个messageId,这个messageId是做什么用的放在之后再解析。withPayload这个容易理解,是用来传递参数的。接下来,webView进行loadUrl(String url),这个url是send(String type, WebView toWebView, Object withPayload, JockeyCallback complete)方法的关键。url的格式是javascript:Jockey.trigger(\"%s\", %d, %s),即调用了Html的window.Jockey.trigger(type, messageId, json)方法,JS会通过type去匹配相对应的函数并且调用,JS层的具体实现这里不讲。

在和JS交互的业务中,往往需要在调用完JS函数后有一个回调,以便通知我们该函数运行完成,可以继续后续操作。JockeyJS已经集成了这一逻辑。当调用send(String type, WebView toWebView, Object withPayload, JockeyCallback complete)时,会将一个自增的messageId和一个JockeyCallback一一对应保存在_callbacks变量中,Java层将messageId和函数名一起传给JS,JS在运行完相关函数后,会使用该messageId通知Java(通知方式见JS调用Java的实现),Java层的JackeyJS通过messageId找到JockeyCallback并调用来完成回调。这一层逻辑不暴露给开发者,开发者只需要关心JockeyCallback的实现,大大方便了回调的处理。

2. JS调用Java的实现

JS调用Java不通过@JavascriptInterface,那是怎么调用的呢?通过JockeyImpl类可以找到,JockeyJS对webView设置了自己的JockeyWebViewClientJockeyWebViewClient的特别之处在于重写了shouldOverrideUrlLoading(WebView view, String url)方法。

public boolean shouldOverrideUrlLoading(WebView view, String url) {	...	if (isJockeyScheme(uri)) {		processUri(view, uri);		return true;	}	...}复制代码

这里isJockeyScheme(uri)对url进行了判断:

public boolean isJockeyScheme(URI uri) {	return uri.getScheme().equals("jockey") && !uri.getQuery().equals("");}复制代码

当url的scheme为jockey时,即url是以jockey://xxx这种格式存在时,JockeyJS会对该url进行拦截,交给应用自己处理,调用processUri(WebView view, URI uri)

public void processUri(WebView view, URI uri)		throws HostValidationException {	String[] parts = uri.getPath().replaceAll("^\\/", "").split("/");	String host = uri.getHost();	JockeyWebViewPayload payload = checkPayload(_gson.fromJson(			uri.getQuery(), JockeyWebViewPayload.class));	if (parts.length > 0) {		if (host.equals("event")) {			getImplementation().triggerEventFromWebView(view, payload);		} else if (host.equals("callback")) {			getImplementation().triggerCallbackForMessage(					Integer.parseInt(parts[0]));		}	}}复制代码

JockeyJS从url中取出host和parts,判断host为"event"时,JockeyJS调用getImplementation().triggerEventFromWebView

protected void triggerEventFromWebView(final WebView webView,		JockeyWebViewPayload envelope) {	final int messageId = envelope.id;	String type = envelope.type;	if (this.handles(type)) {		JockeyHandler handler = _listeners.get(type);		handler.perform(envelope.payload, new OnCompletedListener() {			@Override			public void onCompleted() {				_handler.post(new Runnable() {					@Override					public void run() {						triggerCallbackOnWebView(webView, messageId);					}				});			}		});	}}复制代码

JockeyJS通过envelope.type_listeners拿到对应的JockeyHandler,这些JockeyHandler就是我们初始化JockeyJS时通过on(String type, JockeyHandler ... handler)加入的。接着perform(Map<Object, Object> payload, OnCompletedListener listener)调用doPerform(Map<Object, Object> payload)

protected void doPerform(Map
payload) { for (JockeyHandler handler : _handlers) handler.perform(payload, this._accumulator);}复制代码

可以看到是对我们注册的JockeyHandler进行调用,这样便实现了JS对Java方法的调用。

单单到这一步还没完成JockeyJS的这一调用流程,接下来成JockeyJS会在doPerform(Map<Object, Object> payload)完成后,通过triggerCallbackOnWebView(webView, messageId)回调JS,通知JS层方法已执行完毕,由JS去执行后续操作。

triggerCallbackOnWebView(webView, messageId)的实现类似于send(String type, WebView toWebView, Object withPayload, JockeyCallback complete),在此就不赘述。

回到host的判断,还有一种host为"callback"的情况,此时JockeyJS会调用getImplementation().triggerCallbackForMessage(int messageId)

protected void triggerCallbackForMessage(int messageId) {	try {		JockeyCallback complete = _callbacks.get(messageId, _DEFAULT);		complete.call();	} catch (Exception e) {		e.printStackTrace();	}	_callbacks.remove(messageId);}复制代码

很简单,该方法是通知messageId从_callbacks中取出JockeyCallback并调用,即在上文中提到的send(String type, WebView toWebView, Object withPayload, JockeyCallback complete)接收JS回调的实现。

三、总结

JockeyJS无疑是封装良好的用于JS交互的库,不仅仅适用于Android,也兼容iOS平台。通过webView.loadUrl("javascript:xxx")shouldOverrideUrlLoading(WebView view, String url)方法达到Java和JS的相互调用,并封装了回调逻辑,大大方便业务的开发。当然,随着项目业务需求的增加,JockeyJS还是有可以优化的空间,但是JockeyJS的整体封装值得参考,特别是对于初始项目,可以在JS交互上少走一点弯路。感兴趣的同学也可以继续阅读JockeyJS在JS层和iOS层的代码实现。

转载于:https://juejin.im/post/5b231bc551882574df1303cc

你可能感兴趣的文章
探索测试探索思路
查看>>
Mysql数据库远程授权
查看>>
测试设备商泰瑞达Q3净利6379万 同比减10.72%
查看>>
如何在Ubuntu 13.04, 13.10上安装Sublime Text 3
查看>>
C++语言基础 例程 派生类的声明与构成
查看>>
时间都去哪了?BPC电波授时信号的“零成本”伪造
查看>>
大数据workshop:《云数据·大计算:海量日志数据分析与应用》之《社交数据分析:好友推荐》篇...
查看>>
Python的类方法
查看>>
吐槽一下J2Cache
查看>>
PHP的语言构造器
查看>>
mysql5.6.25及以上下载衔接
查看>>
Node+fs+定时器(node-schedule)+MySql
查看>>
Eclipse环境安装Python插件PyDev
查看>>
【IOS-COCOS2D游戏开发之十九】游戏数据存储的四种常用方式NSKEYEDARCHIVER/NSUSERDEFAULTS/WRITE写入/SQLITE3...
查看>>
git 常用命令
查看>>
logging 日志表的设计
查看>>
sed 常见用法
查看>>
很多人很想知道怎么扫一扫二维码就能打开网站,就能添加联系人,就能链接wifi,今天做个demo(续集)...
查看>>
mysql 常用操作(整理)
查看>>
Java 网络教程: ServerSocket
查看>>