HardBirch

Android构建hybrid应用之js和java互相调用

时间:15-07-14 栏目:Android探究 作者:魔豆先生 评论:0 点击: 1,833 次

需求

大部分APP的发展应该都会有这种经历,一开始用原生搭建一个可用的APP,吸引到不分种子用户,然后不断完善。随着业务的发展,就会发现通过每隔一段时间去发布一个版本,然后等待用户升级更新,已经远远不能满足业务发展的需求。比如运营在某个节日如果突然提出要搞一个活动,要求一天或者两天上线,这时候你就会发现卧槽,这跟不能不可能啊,当然这时候你也要准备好承受运营鄙夷的眼光(这么点需求都搞不定,哼!)。

当然,作为一个有上进心的dev,是肯定不能接收这种事情的发生的,于是你开始寻求解决方案。参考你以前做桌面开发的经验,首先你可能想到的就是使用插件化的思路,把APP改造成一个大容器,各种活动以插件的形式运行在APP中,APP在启动后会从服务器下载最新的各种活动插件。这方面已经有很多的库实现了,不过这不是今天我要讲的主角,后面再说。

我们换个思路,活动这尼玛基本都是一堆随时可以来,生命周期很短的东西,即使用原生的插件写,那也是很蛋疼啊,想想活动那绚丽的UI。。。为毛不用web来做的呢,本来这一坨坨的活动就是Android,iOS,网站都要搞的,H5做一套,全平台都用岂不是很爽。其实你仔细观察以下大部分的APP(淘宝,点评,微信blabla),你会发先他们的活动页面基本都是web做的。

那么问题又来了,我的各种活动页并不是一个简单的web页面,很多时候,我的各种活动需要和APP做各种交互,比如,需要将用户登录的信息传递给活动页面,比如活动页面在参加活动成功之后需要通知APP,然后APP去更新某些原生的UI,这个时候就牵扯到了js和java的通讯了。额,废话了这么多,终于扯到这个问题了。下面我就讲讲Android中js和java交互的几种方式。

使用webView.addJavascriptInterface

Android Webview提供了addJavascriptInterface方法

public void addJavascriptInterface (Object object, String name)

官方文档的描述是这个方法可以将提供的Java对象注入到WebView中。这个对象会被注入到main frame的JavaScript环境中。如果API版本大于等于JELLY_BEAN_MR1,被注入的对象只有public的且被JavascriptInterface注解标注的方法才能在js中使用。API版本小于等于JELLY_BEAN,被注入对象的所有的public方法都能被js访问,包含继承来的方法(这里存在一个很严重的安全漏洞,js中可以通过反射去执行任何java方法)。

注意,注入的对象仅在下次加载页面或者重新载入之后才可用。

先看看Java的代码:

webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new WebAppInterface(this), "Android"); webView.loadUrl("file:///android_asset/index.html"); public static class WebAppInterface { AddJavascriptInterfaceActivity activity; /** Instantiate the interface and set the context */ WebAppInterface(AddJavascriptInterfaceActivity c) { activity = c; } /** Show a toast from the web page */ @JavascriptInterface public void showToast(String toast) { Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show(); } }

再看web页面中js如何调用:

<body> <p> <input type="text" id="username" placeholder="username" /> </p> <p> <input type="button" id="enter1" value="调用Native方法showToast" onclick="showAndroidToast();"/> </p> </body> <script> function showAndroidToast() { var username = document.getElementById("username").value; Android.showToast("Hello " + username); } </script>

上面的代码演示了如果在js中调用Java代码,如果需要从Java中调用js代码,Android是没有提供可以直接使用的机制的,这时候就是要使用webview.loadUrl()方法。

webView.loadUrl("javascript:myjavascriptfunc()");

这里比较坑的一点是,调用loadUrl方法是没有返回值的,如果我们想得到js方法的调用结果,只能自己去实现了。具体的实现思路也很简单,就是在js的myjavascriptfunc中再去调用Java代码,将原本的返回值,传递给调用的Java方法,这样我们就可以在Java代码中拿到js方法的执行结果了。

首先,给我们的activity上添加一个方法,用来接收js方法执行完成之后传递来的参数,并显示一个toast

public void javascriptCallFinished(String result) { Toast.makeText(this, "javascriptCallFinished: " + result, Toast.LENGTH_SHORT).show(); }

然后,给WebAppInterface类添加一个setResult,js里面其实是通过调用这个方法来通知Java方法调用结束的。

@JavascriptInterface public void setResult(String val){ this.activity.javascriptCallFinished(val); } function myjavascriptfunc() { Android.setResult("hoho"); }

最后,在Activity上添加一个button,在button的点击事件里面去调用js的myjavascriptfunc。

callJsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { webView.loadUrl("javascript:myjavascriptfunc()"); } });

点击按钮之后,toast会显示“javascriptCallFinished:hoho”,其中hoho就是myjavascriptfunc的执行结果。

总结

使用addJavascriptInterface应该是实现js和Java通讯的最简单的方法了,也是官方支持的最好的方法。缺点就是API小于17的时候存在严重的安全漏洞,参考乌云的这篇文章,这基本就说明这个方法在生产环境不可用了,除非你的APP最低仅支持API 17(4.2)(牛逼的APP哈)。下一篇中我会讲解如果不使用addJavascriptInterface,哈哈,你应该已经猜到了,就是只使用loadUrl。

看到这里你可能忍不住要吐槽,尼玛,就这样结束了?这个js和Java的互相调用也太简单了吧?如果我的需要调用js中的多个函数,并且每个都要处理返回值,这种写法岂不是啃爹了?难道每个函数处理结果我都要写一个对应的方法?有这些问题,说明你是一个很细心的同学嘛,这个问题我当然是要处理,不然怎么敢在产品上这么坑爹的用呢。

下一篇,我讲讲解如何使用一个消息队列,来满足这种js和Java大量频繁的调用的需求。最终我们的目标就是实现一个和微信的webview中提供的JsBridge一样的框架。做过微信公众号开发的同学,应该知道微信的webview提供了一套非常强大的和Native代码通讯的功能,当然我们最终实现的这个框架也参考了微信的代码实现。

有兴趣的同学可以关注我的这个JsBridge项目,这是我们线上产品使用的代码的改良版本。

本篇到这里就结束了,相关的demo代码可以去我的github下载

声明: 本文由( 魔豆先生 )原创编译,转载请保留链接: Android构建hybrid应用之js和java互相调用

Android构建hybrid应用之js和java互相调用:等您坐沙发呢!

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐