我们知道目前android市场上的一些应用采用的开发方式大致分为三种:Native App、Web App、Hybrid App。本文主要是Hybrid App中实现的主要技术native组件与js的数据交互的理解以及实现。
Android API中提供了WebView组件来实现对html的渲染。所谓的HybridApp开发方式即是汇集了HTML5、CSS3、jS的相关开发技术,以及数据交换格式json/XML。这显然是Web开发工程师的技能。正是因为如此,众多中小企业选择了这种开发方式来减少对android开发工程师的过度依赖,至于这三种开发方式的比较与优劣不在本文考虑之列。
有了WebView这个组件,Android应用开发技术也就转嫁到html与java数据交互上来。说白了就是js与WebView的数据交互,这就是本文所要讨论的。
1. WebView中载入静态页面
将WebView添加到应用中。和原生控件一样,在layout引入WebView控件。代码片段如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/linearLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000" android:orientation="horizontal" > <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
载入页面:
webView = (WebView) findViewById(R.id.webview); webView.loadUrl("file:///file:///android_asset/page.html");
page.html存储在工程文件的assets根目录下。
2. 引入jquery mobile
引入js框架让我们编写的html页面更接近于原生控件的显示效果。目前主流的移动应用js框架有:jquery mobile和sencha touch(jquery mobile与sencha touch的选型不在本文讨论范围)。本文选择使用jquery mobile。
首先,在webview添加对js的支持:
WebSettings setting = webView.getSettings(); setting.setJavaScriptEnabled(true);//支持js
增加对中文的支持:
WebSettings setting = webView.getSettings(); setting.setDefaultTextEncodingName("GBK");//设置字符编码
设置页面滚动条风格:
webView.setScrollBarStyle(0);//滚动条风格,为0指滚动条不占用空间,直接覆盖在网页上
jquery mobile提供的标准页面模板TemplateForJQuery.html:
<!DOCTYPE html> <html> <head> <title>Page Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/jquery.mobile-1.1.1.min.css" /> <script src="js/jquery.js"></script> <script src="js/jquery.mobile-1.1.1.min.js"></script> </head> <body> <div data-role="page"> <div data-role="header"> <h1>Page Title</h1> </div><!-- /header --> <div data-role="content"> <p>Page content goes here.</p> </div><!-- /content --> <div data-role="footer"> <h4>Page Footer</h4> </div><!-- /footer --> </div><!-- /page --> </body> </html>
页面依赖的js库、css等均放在assets目录下,目录组织结构如下:
运行应用后的截图:
下面是button 的截图,与原生控件没什么明显区别,有种以假乱真的感觉:
3. 良好的用户体验
运行我们的应用发现,在拥有大量js的页面被载入时,一直处于等待中,这是很糟糕的用户体验。可以加入进度条解决。注意到webview提供的两个方法:setWebViewClient和setWebChromeClient。其中setWebChromeClient方法正是可以处理progress的加载,此外,还可以处理js对话框,在webview中显示icon图标等。对于处理progress的代码片段如下:
webView.setWebChromeClient(new WebChromeClient() { public void onProgressChanged(WebView view, int progress) {// 载入进度改变而触发 if (progress == 100) { handler.sendEmptyMessage(1);// 如果全部载入,隐藏进度对话框 } super.onProgressChanged(view, progress); } });
其中通过handler 消息机制来处理UI线程的更新:
handler = new Handler() { public void handleMessage(Message msg) {// 定义一个Handler,用于处理下载线程与UI间通讯 if (!Thread.currentThread().isInterrupted()){ switch (msg.what) { case 0: pd.show();// 显示进度对话框 break; case 1: pd.hide();// 隐藏进度对话框,不可使用dismiss()、cancel(),否则再次调用show()时,显示的对话框小圆圈不会动。 break; } } super.handleMessage(msg); } };
对于setWebViewClient方法,一般用来处理html的加载(需要重载onPageStarted(WebView view, String url, Bitmap favicon))、关闭(需要重载onPageFinished(WebViewview, String url)方法)。
setWebViewClient和setWebChromeClient的作用:前者主要用于处理webView的控制问题,如加载、关闭、错误处理等;后者主要处理js对话框、图标、页面标题等。
4. 获取java中的数据
单独构建一个接口,作为处理js与java的数据交互的桥梁,本文封装的代码AndroidToastForJs.java如下:
public class AndroidToastForJs { private Context mContext; public AndroidToastForJs(Context context){ this.mContext = context; } //webview中调用toast原生组件 public void showToast(String toast) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); } //webview中求和 public int sum(int a,int b){ return a+b; } //以json实现webview与js之间的数据交互 public String jsontohtml(){ JSONObject map; JSONArray array = new JSONArray(); try { map = new JSONObject(); map.put("name","aaron"); map.put("age", 25); map.put("address", "中国上海"); array.put(map); map = new JSONObject(); map.put("name","jacky"); map.put("age", 22); map.put("address", "中国北京"); array.put(map); map = new JSONObject(); map.put("name","vans"); map.put("age", 26); map.put("address", "中国深圳"); map.put("phone","13888888888"); array.put(map); } catch (JSONException e) { e.printStackTrace(); } return array.toString(); } }
Webview提供的传入js的方法:
webView.addJavascriptInterface(new AndroidToastForJs(mContext), "JavaScriptInterface");
Html页面jsonData.html设计的部分代码如下:
<script type="text/javascript"> var result = JavaScriptInterface.jsontohtml(); var obj = eval("("+result+")");//解析json字符串 function showAndroidToast(toast) { JavaScriptInterface.showToast(toast); } function getjsonData(){ var result = JavaScriptInterface.jsontohtml(); var obj = eval("("+result+")");//解析json字符串 for(i=0;i<obj.length;i++){ var user=obj[i]; document.write("<p>姓名:"+user.name+"</p>"); document.write("<p>年龄:"+user.age+"</p>"); document.write("<p>地址:"+user.address+"</p>"); if(user.phone!=null){ document.write("<p>手机号码:"+user.address+"</p>"); } } } function list(){ document.write("<div data-role='header'><p>another</p></div>"); } </script> </head> <body> <div data-role="page" > <div data-role="header" data-theme="c"> <h1>Android via Interface</h1> </div><!-- /header --> <div data-role="content"> <button value="say hello" onclick="showAndroidToast('Hello,Android!')" data-theme="e"></button> <button value="get json data" onclick="getjsonData()" data-theme="e"></button> </div><!-- /content --> <div data-role="collapsible" data-theme="c" data-content-theme="f"> <h3>I'm <script>document.write(obj[0].name);</script>,click to see my info</h3> <p><script>document.write("<p>姓名:"+obj[0].name+"</p>");</script></p> <p><script>document.write("<p>年龄:"+obj[0].age+"</p>");</script></p> <p><script>document.write("<p>地址:"+obj[0].address+"</p>");</script></p> </div> <div data-role="footer" data-theme="c"> <h4>Page Footer</h4> </div><!-- /footer --> </div><!-- /page --> </body>
点击say hello按钮运行的截图如下:
补充:webView.addJavascriptInterface(this,"Android");//this:用于调用this里面的public方法,Android:为调用方法的别名
this的环境下写一个方法,必须为public用于html调用
public void startFunction(){}
在HTML中,<a onclick="window.Android.startFunction()">点击调用java代码</a>
window.Android.startFunction()中的window加不加都可以,加上符合编码规范
在java代码中调用html中的js方法,webView.loadUrl("javascript:javacalljs("+"'hello world'"+")");//javascript:为script标签;在html中js方法,javacalljs:function javacalljs(str){}