您好,欢迎访问一九零五行业门户网

为自己搭建一个鹊桥 Native Page与Web View之间的JSBridge实现方式_html/css_WEB-ITnose

说起jsbridge,大家最熟悉的应该就是微信的weixinjsbridge,通过它各个公众页面可以调用后台方法和微信进行交互,为用户提供相关功能。我们就来说说uwp下怎么样实现我们自己的jsbridge。
在win10之前,如果需要实现jsbridge,我们大概有两种方法:
1. window.external.notify 做过webview的小伙伴肯定都熟悉,html页面可以通过window.external.notify将消息发送出去,然后客户端使用webview.scriptnotify事件接收,但是两边都只能用字符串来交流,所以通常我们都会定义好消息格式(比如json)。现在在uwp中使用这种方法有个限制,就是你需要在.appxmanifest里把站点加到content uris中,告诉系统那些域名的js脚本是可以调用windows.external.notify方法的,当然如果是本地js就没有这个限制的,添加方法如下图。
但是我们总会有些特殊需求,比如微信/淘宝应用怎么办?域名随时可能增加,总不能每次都更新manifest,然后更新商店吧!在8.1的时候我们还可以使用webview.allowedscriptnotifyuris在应用中动态添加信任站点,但是win10中这个接口已经废弃了,如果你的应用并不需要频繁/动态更改信任站点,这个方法还是可用的。
后台处理完结果之后,可以通过webview.invokescript/invokescriptasync方法调用当前页面中的js方法:
第一个参数是js方法名,第二个参数是调用这个方法需要的参数。
需要注意的是这个方法很容易出错,一定要注意异常捕获:(, 而且生成的异常基本都是一些0xxxxxx的code。
1 public sealed partial class mainpage : page 2 { 3 bridgeobject.bridge _bridge = new bridgeobject.bridge(); 4 5 public mainpage() 6 { 7 this.initializecomponent(); 8 9 this.wv.scriptnotify += wv_scriptnotify;10 11 this.loaded += mainpage_loaded;12 }13 14 private async void wv_scriptnotify(object sender, notifyeventargs e)15 {16 await (new messagedialog(e.value)).showasync();17 18 //返回结果给html页面19 await this.wv.invokescriptasync(recieve, new[] { hehe, 我是个结果});20 }21 22 private void mainpage_loaded(object sender, routedeventargs e)23 {24 //我们事先写好了一个本地html页面用来做测试25 this.wv.navigate(new uri(ms-appx-web:///assets/html/index.html, urikind.relativeorabsolute));26 }27 }view code
html代码:
1 2 3 4 5 6 7 8 25 26 27 28 call method 129
30
31 32 view code
2. url 是的,你没有看错,我们也可以通过url实现jsbridge,这也是我们在放弃上一种方法之后的一个备选方案,因为手淘就有之前说到的问题,站点可能不是固定的,而更新应用明显不是个明智的选择。具体就是每次html页面需要调用后台code的时候,都发起一次页面跳转,当然跳转的url符合一定的规则,并可以加上参数,然后我们用webview.navigationstarting事件截获这次跳转,并cancel调这次跳转,这样一个看似可行的方案出炉啦,还是热乎的呢!!
代码其实很简单,就是解析url参数,然后再通过webview.invokescript/invokescriptasync方法返回结果给页面(这个方法不针对站点)。
1 private void wv_navigationstarting(webview sender, webviewnavigationstartingeventargs args) 2 { 3 if(args.uri.originalstring.startswith(http://our/jsbridge/url/pattern)) 4 { 5 //是一次jsbridge调用,取消本次跳转 6 args.cancel = true; 7 8 //这里具体解析url的参数 9 }10 }view code
仔细想想。。好像也没什么不对,够动态,够简单。。。但现实总是残酷的,实际使用过程中突然发现,webview的url有最大长度限制,而且这个值比android和ios都要小很多,导致很多参数被截断了,最后只好放弃了。
就在上面两种方案都不能完美适应所有需求的时候,另外一种bulingbuling的方法出现在我们眼前: webview.addweballowedobject ,这个方法是win10中新添加的方法,允许我们把windows runtime对象直接传递给js调用!
下面是这个方法的定义:
public void addweballowedobject(string name, object pobject)
name 是对象在js中对应的全局变量名,通过这个方法传入到html页面中的对象都是挂在js的 window 对象上的, pobject 就是要传入的对象。
首先新建一个windows runtime component工程,添加一个新的类bridge,我们之后就把这个传给也main,看看这个类有什么特殊的。
1 //这个attribute是必须的,有了他我们的对象才能传递给webview 2 [allowforweb] 3 public sealed class bridge 4 { 5 /// 6 /// 提示一条消息 7 /// 8 /// 9 public void showmessage(string msg)10 {11 new messagedialog(msg).showasync();12 }13 14 15 }view code
一切的魔法都在allowforwebattribute这个特性上,有了它,我们的对象就可以传递给webview,但是这里有一点一定要万分小心,必须在navigationstarting调用addweballowedobject方法才可以!(我不会告诉你,我在domloaded事件里折腾了好久。。。)
1 public sealed partial class mainpage : page 2 { 3 bridgeobject.bridge _bridge = new bridgeobject.bridge(); 4 5 public mainpage() 6 { 7 this.initializecomponent(); 8 9 this.wv.navigationstarting += wv_navigationstarting;10 11 this.loaded += mainpage_loaded;12 }13 14 private void mainpage_loaded(object sender, routedeventargs e)15 {16 //我们事先写好了一个本地html页面用来做测试17 this.wv.navigate(new uri(ms-appx-web:///assets/html/index.html, urikind.relativeorabsolute));18 }19 20 private void wv_navigationstarting(webview sender, webviewnavigationstartingeventargs args)21 {22 //ourbridgeobj这个是我们的对象插入到页面之后对象的变量名,这是一个全局变量,也就是window.ourbridgeobj23 this.wv.addweballowedobject(ourbridgeobj, _bridge);24 }25 }view code
现在是见证奇迹的时候了,来看看在js中怎么调用这个对象?(请忽略我这水平不怎么样的html code。。。)
1 2 3 4 5 6 7 8 18 19 20 21 call method 122
23 24 view code
代码都很直接,唯一需要说明的就是一定要注意js中调用方法时首字母都是小写(即使你在后台定义的首字母大写!当然这应该也是为了符合js的使用习惯),来看看结果。
当然如果它只有这点本事的话,并不会让人很激动,毕竟我们以前也可以做到。
继续之前,想想win10之前如果要通过jsbridge调用后台代码实现一个异步操作会怎么实现呢?
1). 首先我们的js调用和webview.invokescript是分开,所以通常我们要为每一次js调用生成一个id
2). 后台完成操作之后,通过invokescript方法返回结果时,需要把本次调用id传回去,告诉页面这个哪次调用的结果
3). 然后js再根据这个id回调继续之前的操作。
但是现在我们可以抛弃那些繁琐的步骤了,我们的windows runtime component支持异步(iasyncaction/iasyncoperation),而js又支持 promise ,结合在一起,你懂的!
先给我们的类添加一个简单的异步方法。
1 //这个attribute是必须的,有了他我们的对象才能传递给webview 2 [allowforweb] 3 public sealed class bridge 4 { 5 /// 6 /// 提示一条消息 7 /// 8 /// 9 public void showmessage(string msg)10 {11 new messagedialog(msg).showasync();12 }13 14 public windows.foundation.iasyncoperation givemeanobject(int num)15 {16 return task.run(async () =>17 {18 //延迟3秒钟,模拟异步任务:)19 await task.delay(3000);20 21 return ++num;22 }).asasyncoperation();23 }24 }view code
接下来我们在js端,用 promise.then 来等待结果。
1 2 3 4 5 6 7 8 33 34 35 36 call method 137 call method 238 39
40 41 view code
运行起来,等待3秒之后,结果出来了!
最后如果你觉得写component限制太多的话(继承都不让用。。),可以使用接口定义方法,然后在类库中实现这些方法也是一个不错的方案,下面是一个比较简单的实现供参考。
我们的jsbridge接口,包含我们准备提供的方法。
1 /// 2 /// 用来定义jsbridge中实现的方法3 /// 4 public interface ibridgemethods5 {6 iasyncoperation givemmeanobject(int num);7 void showmessage(string message);8 }view code
修改我们的bridge类,所有的方法都通过上面的接口来提供。
1 //这个attribute是必须的,有了他我们的对象才能传递给webview 2 [allowforweb] 3 public sealed class bridge 4 { 5 private ibridgemethods _methods = null; 6 7 8 /// 9 /// 提示一条消息10 /// 11 /// 12 public void showmessage(string msg)13 {14 _methods?.showmessage(msg);15 }16 17 public iasyncoperation givemeanobject(int num)18 {19 return _methods?.givemmeanobject(num);20 }21 22 /// 23 /// 初始化个方法的实现24 /// 25 /// 26 public void init(ibridgemethods obj)27 {28 _methods = obj;29 }30 }view code
其它类似信息

推荐信息