混合开发,原生+html5
来源:互联网 发布:网络剪刀手 win7 编辑:程序博客网 时间:2024/05/19 23:27
混合开发也是一种很好的开发方式,比如这不京东618又来了,做做活动页,活动过后就换下,不用经过应用商店,方便快捷简洁。
这里记录下工作中用到的混合开发的场景:
一个专门的应用,叫订单中心,除了处理本应用的订单外,还承接三方应用,对订单数据详情进行展示,并能从中调用设备打印功能
对于数据的展示,这没什么好说的了,该页面是一个WebView就可以了
对于从html中调用设备的原生函数,就需要注意native与h5的交互了。
先上承载页面,一个activity
package com.yunnex.eshop.hybrid;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.webkit.WebSettings;import android.webkit.WebView;import com.yunnex.eshop.R;import com.yunnex.eshop.hybrid.jsbridge.RainbowBridge;import com.yunnex.eshop.hybrid.jsbridge.core.JsBridgeWebChromeClient;public class OrderDetailH5Activity extends AppCompatActivity{ private WebView mWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_order_detail_h5); mWebView = (WebView) findViewById(R.id.webview); WebSettings settings = mWebView.getSettings(); settings.setJavaScriptEnabled(true); RainbowBridge.getInstance() .clazz(JsInvokeJavaScope.class) .inject(); mWebView.setWebChromeClient(new JsBridgeWebChromeClient()); //url can be from any other app or web site //in a word,this is a h5 ui Intent intent = getIntent(); String uri = intent.getStringExtra("uri");// uri="file:///android_asset/test.html";// uri="http://m.zb25.com.cn/shebei/printInfo"; uri="http://192.168.9.151:8080/Hybrid/functions.html"; mWebView.loadUrl(uri); }}
上面用到了RainBridge这个交互框架,关于该框架的对于交互的原理的讲解,使用,请参考
http://zhengxiaoyong.me/2016/04/20/Native%E4%B8%8EH5%E4%BA%A4%E4%BA%92%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/
交互的协议,可简单概括为:
jsbridge://class:port/method?params //(schema://host:port/path?params)
jsBridge为固定,port自动生成不用管
class由设备端提供,也为固定值
因此h5调用native时只需关注method(方法名)与params(params是一串json字符串)
举个例子,如果h5触发设备的登录逻辑
<button
onclick="RainbowBridge.callMethod('JsInvokeJavaScope','login',{'allowEscape':'0','userName':'yunnex','uri':'yunnex://native/login','requestCode':'8'},function(msg){alert(JSON.stringify(msg))});">
登录
</button>
RainbowBridge为设备端提供的RainbowBridge.js文件,callMethod为对外的一级入口,固定值
第一个参数 JsInvokeJavaScope为设备端业务处理类,固定值
第四个参数为处理的回调
因此h5调用设备只需关注第二个参数,该参数即为业务方法名称login(由设备端提供),和第三个参数(业务方法参数,组装为json串,{'allowEscape':'0','userName':'yunnex','uri':'yunnex://native/login','requestCode':'8'})
业务方法参数遵循的规则如下:
参数params保留字(uri,allowEscape,requestCode)
uri:页面需跳转时指定的值
本应用内跳转allowEscape可忽略,其他应用跳转allowEscape需要指定为1
requestCode,请求码,如需要,指定
非保留字参数,前端与设备端商量一致即可(如userName)
设备端处理后若有返回值,格式为
var resultData = {
status: {
code: 0,//0成功,1失败
msg: '请求超时'//失败时候的提示,成功可为空
},
data: {}//数据,无数据可以为空
};
返回值会由回调函数function(msg){...}处理
uri="http://192.168.9.151:8080/Hybrid/functions.html";
这个是访问我本地服务器,function.html在webroot根目录下,内容如下
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>JsBridge</title>
<meta name="author" content="zhengxiaoyong">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1, target-densitydpi=medium-dpi, user-scalable=no">
<meta property="og:site_name" content="JsBridge"/>
<script src="RainbowBridge.js" type="text/javascript"></script>
<style>
.entry{
-webkit-padding-start: 30px;
}
.entry li {
line-height: 29px;
margin-left: -10px;
}
.entry li div{
margin-right: 10px;
padding-left: 8px;
padding-top: 8px;
padding-bottom: 8px;
background: #222222;
color: white;
word-break: break-all;
-ms-word-wrap: break-word;
word-wrap: break-word;
line-height: 15px;
font-size: 10pt;
}
.entry li button{
margin-top: 5px;
width: 60px;
height: 35px;
color: #111111;
}
</style>
</head>
<body>
<ul class="entry">
<li>
打印<br/>
<br/>
<div>
我是Js调起的打印
</div>
<button
onclick="RainbowBridge.callMethod('JsInvokeJavaScope','print',{'orderId':'20850258804864716800'},
function(msg){alert(JSON.stringify(msg))});">
打印
</button>
</li>
<br/>
</ul>
</body>
</html>
关键代码第一行 <script src="RainbowBridge.js" type="text/javascript"></script>
关键代码第二行 onclick="RainbowBridge.callMethod('JsInvokeJavaScope','print',{'orderId':'20850258804864716800'},
function(msg){alert(JSON.stringify(msg))});">
第一行表示该html页面引入的js文件,处理交互动作如onclick
第二行是交互动作的写法规范,参考交互协议
Rainbridge.js内容如下:
/** * * native结果数据返回格式: * var resultData = { status: { code: 0,//0成功,1失败 msg: '请求超时'//失败时候的提示,成功可为空 }, data: {}//数据,无数据可以为空}; 协定协议:rainbow://class:port/method?params; params是一串json字符串 */(function () { var doc = document; var win = window; var ua = win.navigator.userAgent; var JS_BRIDGE_PROTOCOL_SCHEMA = "rainbow"; var increase = 1; var RainbowBridge = win.RainbowBridge || (win.RainbowBridge = {}); var ExposeMethod = { callMethod: function (clazz, method, param, callback) { var port = PrivateMethod.generatePort(); if (typeof callback !== 'function') { callback = null; } PrivateMethod.registerCallback(port, callback); PrivateMethod.callNativeMethod(clazz, port, method, param); }, onComplete: function (port, result) { PrivateMethod.onNativeComplete(port, result); } }; var PrivateMethod = { callbacks: {}, registerCallback: function (port, callback) { if (callback) { PrivateMethod.callbacks[port] = callback;//callbacks.port=callback } }, getCallback: function (port) { var call = {}; if (PrivateMethod.callbacks[port]) { call.callback = PrivateMethod.callbacks[port]; } else { call.callback = null; } return call; }, unRegisterCallback: function (port) { if (PrivateMethod.callbacks[port]) { delete PrivateMethod.callbacks[port]; } }, onNativeComplete: function (port, result) { var resultJson = PrivateMethod.str2Json(result); var callback = PrivateMethod.getCallback(port).callback; PrivateMethod.unRegisterCallback(port); if (callback) { //执行回调 callback && callback(resultJson); } }, generatePort: function () { return Math.floor(Math.random() * (1 << 50)) + '' + increase++; }, str2Json: function (str) { if (str && typeof str === 'string') { try { return JSON.parse(str); } catch (e) { return { status: { code: 1, msg: 'params parse error!' } }; } } else { return str || {}; } }, json2Str: function (param) { if (param && typeof param === 'object') { return JSON.stringify(param); } else { return param || ''; } }, callNativeMethod: function (clazz, port, method, param) { if (PrivateMethod.isAndroid()) { var jsonStr = PrivateMethod.json2Str(param); var uri = JS_BRIDGE_PROTOCOL_SCHEMA + "://" + clazz + ":" + port + "/" + method + "?" + jsonStr; win.prompt(uri, ""); } }, isAndroid: function () { var tmp = ua.toLowerCase(); var android = tmp.indexOf("android") > -1; return !!android; }, isIos: function () { var tmp = ua.toLowerCase(); var ios = tmp.indexOf("iphone") > -1; return !!ios; } }; for (var index in ExposeMethod) { if (ExposeMethod.hasOwnProperty(index)) { if (!Object.prototype.hasOwnProperty.call(RainbowBridge, index)) { RainbowBridge[index] = ExposeMethod[index]; } } }})();该文件也在webRoot根目录下。
理一下服务端执行逻辑,当触发onClick事件后,将会告知设备端调用print函数,打印的订单参数为orderId=20850258804864716800
onclick="RainbowBridge.callMethod('JsInvokeJavaScope','print',{'orderId':'20850258804864716800'},
function(msg){alert(JSON.stringify(msg))})
设备端JsInvokeJavaScope类将会调用print函数
public static void print(WebView webView, JSONObject data, JsCallback callback){ initPrintData(webView.getContext(), data);}
private static void initPrintData(final Context context, JSONObject params){ /** * 解析前端参数 */ String orderId = ""; try { orderId = params.getString("orderId"); } catch (JSONException e) { e.printStackTrace(); } if (TextUtils.isEmpty(orderId)) { Toast.makeText(context, "订单id为空", Toast.LENGTH_SHORT).show(); return; } IOrderModel orderModel = new OrderModelImpl(); orderModel.getOrderPrintData(context, orderId, new IOrderModel.OnGetOrderPrintDataListener() { @Override public void onSuccess(List<PrintData> printDatas) { handleData(context,printDatas); } @Override public void onFailed(String reason) { Toast.makeText(context, reason, Toast.LENGTH_SHORT).show(); } });}
设备端请求打印数据,然后自己就可以愉快的打印啦
- 混合开发,原生+html5
- ios 开发之 原生+html5混合开发 [[ 方法互调 ]]
- 浅谈App原生开发、混合开发及HTML5开发的优劣
- 混合开发-利用Cordova插件实现HTML5 与 原生代码的连接
- 开发移动应用程序的三种选择:原生、HTML5或混合
- WebRTC原生开发和混合开发优缺点
- Android和HTML5混合开发
- unity与原生android混合开发
- 混合开发原生嵌套H5页面
- 混合开发-webview和原生交互
- iOS原生项目集成Cordova混合开发
- Android:前端与原生混合开发尝试
- Web开发、原生开发、混合开发的区别优势:
- Html5 + android原生 混合式开发(一)
- Html5 + android原生 混合式开发(二)
- Android原生开发与HTML5交互
- HTML5原生WebGL开发系列教程
- Html5 + android原生 混合式开发(一)
- 进程间通信----信号量
- New Year Table
- EL表达式
- winpe下开启vs远调器
- 剑指offer:第29题寻找数组中出现次数超过一半的数字
- 混合开发,原生+html5
- 异常—Cause: java.sql.SQLException: Data source is closed
- POJ
- 【算法】程序猿不写代码是不对的67
- OpenJ_Bailian
- 2017年6月7日头条争夺战:微信公众号居然公然和高考抢头条?!
- Ubuntu14.04下OpenCV安装
- #QGIS源码官方编译指南
- 解决Caffe训练过程中loss不变问题