首页 > 代码库 > 浅谈混合开发与Android,JS数据交互
浅谈混合开发与Android,JS数据交互
本文是作者原创,如转载请注明出处!
一.概论
现在时代已经走过了移动互联网的超级火爆阶段,市场上移动开发人员已经趋于饱和,显然,只会原生APP的开发已不能满足市场的需求,随着H5的兴起与火爆,H5在原生APP中的使用越来越广泛,也就是我们常说的混合开发(Hybrid APP).最新很火的微信小程序相信大家都是知道的,实际上微信小程序加载的界面就是一个HTML5的界面,HTML5界面在一些电商类的APP中主要承担展示数据的作用,但是他的作用并不仅限于此,最起码js调用原生方法和原生调用js的方法是必须的,我们看到的现在微信小程序中的膜拜单车入口,进入后的扫码功能,就是js调用安卓原生的摄像头进行扫码的,另外分享功能以及点击js中的一个按钮跳转到APP原生界面这些需求都是很常见的,所以js和原生方法的相互调用是必须会的.最近做HTML5相关的地图APP,正好用到了相关的知识,总结下项目中遇到的坑.
二.JS与Android交互
1.Android如何加载一个H5界面到APP?
目前比较成熟的方案是采用Android封装好的中间件WebView.
加载方式
//创建一个WebView,也可以直接在布局中写WebView控件
WebView mWebView = new WebView(mContext);
//加载网络上的一个url
String url = "https://www.baidu.com";
mWebView.loadUrl(url);
//加载本地的一个url,asserts目录下的
mWebView.loadUrl("file:///android_asset/index.html");
WebView的一些重要设置
//先获取到WebSettings控制类
WebSettings settings = mWebView.getSettings();
//设置支持javascript
settings.setJavaScriptEnabled(true);
//设置为false表示将图片调整为适合WebView的大小
settings.setUseWideViewPort(false);
//设置可以访问文件
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
//设置js支持数据库
settings.setDatabaseEnabled(true);
//设置js支持window.localStorage,如果不设置,js中获取的window和localStorage都是null,之前我就被坑了,项目中js界面用到了window.localStorage,然后我和js采用alert的方式调试的过程中,发现这个方法一直报错才找到的这个api.
settings.setDomStorageEnabled(true);
mWebView.loadUrl("file:///android_asset/index.html");
//添加了一个java和js交互的接口,android字符串相当于一个Brige桥梁的作用,安卓4.2以后增加了@JavascriptInterface接口,只有代码@JavascriptInterface注解的方法js才能调用,之前是被注入的类和从父类继承的所有的public的方法都能访问,这也是进一步保证APP的安全性.
mWebView.addJavascriptInterface(mSnMap, "android");
//设置WebView是否加载完成的监听
mWebView.setWebViewClient(new WebViewClient() {
@Override
//WebView加载完成会调用的方法
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
@Override
//WebView开始加载调用的方法
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
@Override
//WebView的界面可见时调用的方法
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
}
@Override
//是否要拦截js和java调用的方法
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return true;
}
});
//该设置保证加载界面时会自动调用系统的浏览器,而不是提示给用户让用户去选择哪个浏览器
mWebView.setWebChromeClient(new WebChromeClient(){
//对应js中的alert()方法,可以重写该方法完成与js的交互
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
//对应js中promt(),可以重写该方法完成与js的交互
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
//对应js中的console.log方法,可以重写该方法完成与js的交互
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
return super.onConsoleMessage(consoleMessage);
}
});
java调用js的方法
//比如我要调用addline(jsonline)方法,这里给出我写的一个示例
//WebView规定java和js交互的时候前面的字段是javascript:+方法名
//注意:传变量的时候注意要给字符串加上两个单引号,传递方式就是我下面写的那种规范
public void addLine(Line line) {
if (line != null) {
jsonLine = mGson.toJson(line);
mWebView.loadUrl("javascript:addLine(‘" + jsonLine + "‘)");
}
}
js调用java的方法
//这里介绍一种最简单的方式,当然也可以自己使用伪协议去封装
//1.添加一个交互的接口,mSnMap表示要注入的java类,android是桥接字符串,js调用的使用就使用这个字符串进行交互
mWebView.addJavascriptInterface(mSnMap, "android");
//2.在js中调用,比如要调用本地读取二进制文件的一个方法
@JavascriptInterface
public String readshape(String mapshape) {
Gson gson = new Gson();
MapShape mapShape = gson.fromJson(mapshape, MapShape.class);
String shapeBase64 = SEInterfacePvReader.getMapShape(mapShape);
System.out.println(shapeBase64);
return shapeBase64;
}
//3.js调用并获取返回值,另外解释一下,js调用java的方法是在WebView的一个独立的后台线程中,另外这里js调用Android的方法时传递参数只能传一个,而且java与js交互的方法是不支持二进制流交互的,只支持简单的基本数据类型,比如int,boolean,String,数组也是不支持的,数组可以采用json字符串的形式
var bufferData = http://www.mamicode.com/android.readshape(json);
三.数据结构,文件读取
文件读取这块内容,因为矢量数据的存储结构不是我们公司的人设计的,是和我们公司合作的人设计的,他们的服务器代码采用的是C++结合lua脚本语言共同编写的,涉及到数据结构的具体详情不变透漏,主要说下C++和java数据结构的差别以及读取数据时选择的API.
1.C++中的char和java中的char
C++中的char与IOS object-c中的char一样,都是占一个byte
java中的char占2个byte
所以在数据读取过程中c++经常创建一个vector buf就等价于java中读取文件时创建的byte数组.这里用到了java中int转byte数组的工具类.
//具体使用高位到低位还是低位到高位根据具体数据结构而定
public static byte[] intToByteArray(int i) {
byte[] result = new byte[4];
//由高位到低位
// result[0] = (byte)((i >> 24) & 0xFF);
// result[1] = (byte)((i >> 16) & 0xFF);
// result[2] = (byte)((i >> 8) & 0xFF);
// result[3] = (byte)(i & 0xFF);
//低位在前
//由低位到高位
result[0] = (byte) (i & 0xFF);
result[1] = (byte) ((i >> 8) & 0xFF);
result[2] = (byte) ((i >> 16) & 0xFF);
result[3] = (byte) ((i >> 24) & 0xFF);
return result;
}
/**
* byte[] 转化为int[] ,低位在前的方式
* @param title
* @return
*/
public static int[] byteArrayToIntArray(byte[] title) {
int[] array = new int[title.length / 4];
byte[] temp = new byte[4];
int k = 0;
for (int i = 0; i < title.length; i++) {
temp[i % 4] = title[i];
if (i > 0 && (i+1) % 4 == 0) {
array[k] = byteArrayToInt2(temp);
k++;
}
}
return array;
}
/**
* byte数组转化为int值的工具类,低位在前
*
* @param b
* @return byte[]转化为的int数
*/
public static int byteArrayToInt2(byte[] b) {
int MASK = 0xFF;
int result = 0;
result = b[0] & MASK;
result = result + ((b[1] & MASK) << 8);
result = result + ((b[2] & MASK) << 16);
result = result + ((b[3] & MASK) << 24);
return result;
}
2.文件的读取可以选择RandomAccessFile类和FileInputStream类
两个类都能实现读取二进制文件的效果,同时两个类中都有定位到文件的某个位置的方法,但是他们是有区别的.
FileInputStream的skip()方法是相对于当前位置定位的
RandomAccessFile的seek()方法则可以定位到文件的任意位置
下面用代码举个例子
fis.skip(10);
fis.skip(5);
//执行完这两行代码后读取的输入流FileInputStream定位到文件的第15个字节处
raf.seek(10);
raf.seek(5);
//这两行代码的最终结果是RandomAccessFile最终定位到文件的第5个字节处.
//另外需要注意的是:RandomAccessFile是继承于Object的,只是他实现了DataOutput, DataInput,所以可以读文件也可以写文件.而FileInputStream是继承于InputStream的,只能读文件,而我在项目中读文件采用的是InputStream读,ByteArrayOutputStream作为输出流存储数据.
java和js如何交互文件
java和js是不支持直接的二进制流的交互,所以我们就换了一种方式进行交互,将二进制数据读取后使用Base64算法转化为字符串,将字符串传递给js,js再使用Base64算法反编码为二进制数据流.当然除了这种方式外,还有其他的思路,比如移动端开发一个本地的服务器给js发请求,完成数据的交互.
四.项目总结
目前来说,原生与H5的混合开发相对比较成熟,但是现在没有任何一个工具能够在js与java之前完成debug调试,所以项目上遇到问题只能采用日志和alert弹框的方式一步一步排查代码中的错误,调试起来复杂又繁琐,而且java和js在数据交互上依赖浏览器内核,性能上也存在问题,希望不久的将来,技术上能够突破这些瓶颈.地图的开发还是使用原生调用c++的OpenGL绘制矢量地图比较靠谱,之前研究过1个月的OpenGL开发,因为太难以及项目进度比较赶的原因没有继续下去,接下来会抽出更多的时间去完成OpenGL的底层绘制地图,后续的博客也会分享OpenGL的学习之路.
奉上我很喜欢的一句话,与君共勉:比你聪明的人比你还要努力,你又有什么理由不努力呢?
如有不足之处请指正评论,谢谢!
浅谈混合开发与Android,JS数据交互