Android 混合开发 的一些心得。
其实所谓这个混合开发,也就是hybird,就是一些简单的,html5和native 代码之间的交互。很多电商之类的app里面都有类似的功能,
这种东西其实还是蛮重要的,主要就是你有什么功能都可以进行热部署,不需要再重新发版本。下面就简单介绍一下这种技术。
我们首先看下面一个场景,我们打开网易云音乐的app 里面的积分商城,(此时实际上是一个webview去加载了一个html界面。)
然后在显示出来的界面里面点击一下我的订单,因为我们没有登录过,所以此时自动给我弹出了native的登录界面。你看这就是一个
典型的html和native 进行交互的一个场景。为了让大家感受的更深一些,可以看一下下面的gif 操作过程:

经过简单的抓包,我们可以知道 这个webview访问的地址是:http://music.163.com/store/m/product/index
我们在chrome浏览器里 直接打开这个链接 然后也点击我的订单 你会发现:

所以我么继续查看网页源代码,并且对js进行解压缩以后就会发现下面的代码了:
Js.fg = function(Jt) {
var Jv = JC.cr(Jt, "d:action");
switch (Jq.bv(Jv, "action")) {
case "gopage":
if (!this.fv.userId || this.fv.userId <= 0) {
location.href = "orpheus://welfare/login";
return
} else {
location.href = Jq.bv(Jv, "destination")
}
break
}
};
到这应该可以理解了,就是点击了我的订单以后 js的功能把超链接定位成orpheus://welfare/login了。
所以我们可以继续才想到,网易云音乐的app 就是在这个webview里面 捕捉到了这个超链接的信息以后 然后跳转到
自己定义的activity!这就是这个功能的实现原理。
那么我们就依葫芦画瓢来试着仿照一下 能否实现这个功能。我们主要是在webview 上写一些代码:
wb=(WebView)findViewById(R.id.wb);
wb.getSettings().setJavaScriptEnabled(true);
wb.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.contains("orpheus://welfare/login")) {
Intent intent=new Intent();
intent.setClass(TestNetWebViewActivity.this,LoginActivity.class);
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
wb.loadUrl(URL);
然后看一下 是否能像网易云音乐那样实现我们想要的功能:
看下实际运行的gif:

这个方案可以看到是完全可行的。但是这个方案 依旧是有缺陷的,你只能适用于这种简单的情况,
而且他的原理实际上就是利用webview 重新访问一个新url的时候 对新的url 进行分析 然后
决定自己下一步该做什么,也就是说这个js---java代码的调用过程完全依托于对url的字符串的分析。
所谓再复杂一些的场景这个方案就hold不住了!所以我们需要一个新的方案。能让js 方便愉快的
传值到我们的java代码里面!
我们首先在assets这个android路径下面 放一个我们自己写的html代码:
<!DOCTYPE html>
<html>
<head>
<title>JavaScript View</title> <script type="text/javascript"> function showToast(){
var message = document.getElementById("message").value;
var lengthLong = document.getElementById("length").checked; /*
调用java里的makeToast方法,注意这里的app 就和addJavascriptInterface这个函数里的
第二个参数值要保持一致,且大小写敏感
*/
app.makeToast(message, lengthLong);
return false;
} /*
这个很好理解,就是当你这个html加载完成的时候 把表单的submit提交定位到js的 showToast方法里面
就理解成方法的重定向即可
*/
window.onload = function(){
var form = document.getElementById("form");
form.onsubmit = showToast;
}
</script>
</head> <body> <form id="form">
Message: <input id="message" name="message" type="text"/><br />
Long: <input id="length" name="length" type="checkbox" /><br /> <input type="submit" value="Make Toast" />
</form> </body>
</html>
然后把我们的java 代码稍作修改:
wb = (WebView) findViewById(R.id.wb);
wb.getSettings().setJavaScriptEnabled(true);
wb.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app");
wb.loadUrl("file:///android_asset/web.html");
class WebViewJavaScriptInterface {
private Context context; public WebViewJavaScriptInterface(Context context) {
this.context = context;
} @JavascriptInterface
public void makeToast(String message, boolean lengthLong) {
Toast.makeText(context, message, (lengthLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT)).show();
} }
然后看一下跑起来的效果:

可以看出来我们从js这边完美调用java代码的 方案就成功了。
但是实际上呢,这个addJavascriptInterface 方法在4.2 以下呢,是有一个很严重的安全漏洞的,
我们上面的代码 你看到了 我是有一个注解在哪里的,但是如果你的手机是4.2以下的系统,这种系统
是不会检测你那个方法是否有注解的,所以原则上来说 对于4.2以下的系统来说,这个方法可以调用
任何你手机里的任何方法(当然是通过反射)。有兴趣的同学可以看一下这个链接:
http://jaq.alibaba.com/blog.htm?id=48
所以除非你做的app 不支持4.2以下的系统,否则我们认为 这个方案也是有缺陷的。
而且这个方法 还有一个不方便的地方在于,你js是可以调用java了可以调用native代码了,
但是你js调用完java代码以后 无法回调了。我如果想js调用完java代码以后马上进行回调js代码的操作
就无法做到了。有些人可能不明白 回调js 代码无法起作用是什么意思,可以接着看下面的例子。
首先我定义一个按钮,这个按钮就干一件事 就是通过java代码去调用js代码:
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
wb.loadUrl("javascript:display_alert()");
}
});
然后在我们js调用java native函数里面 也写一个这样类似的代码:
@JavascriptInterface
public void makeToast(String message, boolean lengthLong) {
Toast.makeText(context, message, (lengthLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT)).show();
wb.loadUrl("javascript:display_alert()"); }
下面看下运行效果:

所以你看 直接在按钮那边通过java来调用js是可以的,但是你要是通过js调用java 再在java的代码里回调js代码
那就完全无效了。
所以我们下面要解决的问题 主要就是2块:
第一:让js能够安全的调用java代码,主要是对于4.2版本以下的手机来说
第二:让js调用java以后 依旧可以回调js,这是对于所有手机来说的。
关于这种情况的解决方案,我也找了很久,调研了很久。基本上都是通过
WebChromeClient.onJsPrompt 来完成对应的功能。
并且流程就是如下几步:
1.我们假设你js要调用的java native代码 是a 这个类的 a1 a2 a3 3个方法。
2.利用反射机制 把a1 a2 a3 这3个方法 给保存成字符串,存在一个str里面
3.找机会把这个还有对象方法信息的str 转成我们需要的js代码 然后将这个js 代码注入到webview 要加载的html源码里面!
4.这样js就只能执行 注入后的修改过的html代码里的 ”js代码了“ 也就是说 你无法利用js 调用任何方法,只能通过前面3步 注入的js代码 来调用对应的native方法
原理上隔绝了 前面说的4.2以下的 漏洞。
5.js代码成功注入以后 ,就会通过onpromt方法 来完成jscalljava的这个过程。包括要执行的方法名字,参数类型啥之类的都会检查一遍。再次杜绝了4.2以下的那个漏洞,
并且从原理上 可以在java中任意时间 场景回调我们的js代码!
那目前来看 基本上所有的hybrid开发 都是上面这个流程,而且要兼容4.2以下的sdk的时候 基本上我反编译了很多app 都是利用的http://www.pedant.cn/2014/07/04/webview-js-java-interface-research/
这篇文章提到的https://github.com/pedant/safe-java-js-webview-bridge 这个开源库。
但是,实际上这个开源库 并不完美,有一点点小缺陷,而且一直没有得到很好的解决,(所以很多人转载文章或者写blog的时候很不负责任,第一个人怎么写他自己就怎么抄 也不验证。)这其中就是因为有一段代码:
public void onProgressChanged(WebView view, int newProgress) {
//为什么要在这里注入JS
//1 OnPageStarted中注入有可能全局注入不成功,导致页面脚本上所有接口任何时候都不可用
//2 OnPageFinished中注入,虽然最后都会全局注入成功,但是完成时间有可能太晚,当页面在初始化调用接口函数时会等待时间过长
//3 在进度变化时注入,刚好可以在上面两个问题中得到一个折中处理
//为什么是进度大于25%才进行注入,因为从测试看来只有进度大于这个数字页面才真正得到框架刷新加载,保证100%注入成功
if (newProgress <= 25) {
mIsInjectedJS = false;
} else if (!mIsInjectedJS) {
view.loadUrl(mJsCallJava.getPreloadInterfaceJS());
mIsInjectedJS = true;
StopWatch.log(" inject js interface completely on progress " + newProgress);
}
super.onProgressChanged(view, newProgress);
}
你可以看一下 这个注入的时机问题。第七行,这个地方是有问题的,因为大家都知道实际上你webview的性能一直以来都不是太好,还有很多机能很差 或者rom 优化很差的 webview
根本就是一团坑,所以这个里面 类似于 硬编码的 这个注入过程 是不太完美的。在少部分机型 以及少部分场景中,这里会一直注入失败的。导致整个框架都不可用。
所以有代码洁癖的同学要注意了,这个网上流传最广的开源方案 目前是有缺陷的。要慎用~不过这种开源方案 能cover住百分之95以上的手机 我觉得也还行了。
所以目前来看,并没有一个特别有效而且安全完美的方案来规避这个问题。有人说微信hybrid 做的不错,实际上微信我看过他的js sdk。实际上啊,微信并不是用的我们所说的prompt方法
他还是和网易那个一样 通过拦截url 分析url 来执行相应的操作的。native 回调js代码也是走的js里的_handleMessageFromWeixin 这份方法。有兴趣的同学可以去看下微信的做法。
但你其实想一想 微信这个方法也是有缺陷的,因为url是可以伪造的,好在微信自己会在native代码里 验证他的appid。所以一定程度上可以避免大部分的攻击。
Android 混合开发 的一些心得。的更多相关文章
- uni-app&H5&Android混合开发三 || uni-app调用Android原生方法的三种方式
前言: 关于H5的调用Android原生方法的方式有很多,在该片文章中我主要简单介绍三种与Android原生方法交互的方式. 一.H5+方法调用android原生方法 H5+ Android开发规范官 ...
- android混合开发,webview的java与js互操作
android原生应用,用webview加载应用中的网页,并且java代码与js代码可以互相操作. 这是混合开发的基石,最基本也最重要的东西,实验代码在这里. 概括说说—— java调js:调用web ...
- Flutter与Android混合开发及Platform Channel的使用
相对于单独开发Flutter应用,混合开发对于线上项目更具有实际意义,可以把风险控制到最低,也可以进行实战上线.所以介绍 集成已有项目 混合开发涉及原生Native和Flutter进行通信传输,还有插 ...
- Android混合开发,html5自己主动更新爬过的坑
如今使用混合开发的公司越来越多,尽管出现了一些新技术,比方Facebook的react native.阿里的weex,但依旧阻挡不了一些公司採用h5的决心.当然,这也是从多方面考虑的选择. 在三年前就 ...
- [Hybrid App]--Android混合开发,Android、Js的交互
AndroidJs通信 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !imp ...
- uni-app&H5&Android混合开发一 || 最全面的uni-app离线打包Android平台教程
前言: 为什么会写这么一个教程,因为很久之前做过一个对接银行POS我们的系统是使用的H5开发的app应用.但是假如对结果银行相关业务的小伙伴应该都清楚,银行的业务相对于其他的对接方而言安全性比较高,而 ...
- Unity和Android混合开发
通用的流程 https://blog.csdn.net/zhangdi2017/article/details/65629589 应用场景 Unity游戏中一些功能需要安卓系统的支持,如搜索wifi等 ...
- flutter 与 android 混合开发
现有的混合开发方式,都是存flutter项目在android系统或者iOS上面跑. 但是,实际情况是,我们需要在一个成熟的native项目上面,跑几个flutter页面,逐步的进行flutter的融合 ...
- Flutter + Android 混合开发
JIT (Just In Time) 即时编译器, 边执行边编译 程序运行时,JIT 编译器选择将最频繁执行的方法编译成本地代码.运行时才进行本地代码编译而不是在程序运行前进行编译 AOT可以理解为“ ...
随机推荐
- UVA 11133 - Eigensequence DP
Given an increasing sequence of integers a1, a2, a3, . . . , ak, the E-transform produces a sequence ...
- 关于linux下rar文件的解压缩操作
在linux系统下.本身没有对rar文件操作的命令,如果需要对rar格式的文件操作,我们需要安装第三方的软件rar以及unrar. 1.linux下rar管理软件下载的官方地址为:http://www ...
- CSS3做小三角形
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXgAAAA2CAIAAABC2hVZAAAgAElEQVR4nKzcd3cbV57web+1p20FW8
- Project Euler P105:Special subset sums: testing 特殊的子集和 检验
Special subset sums: testing Let S(A) represent the sum of elements in set A of size n. We shall cal ...
- JSTL标签库中fmt标签,日期,数字的格式化
首先介绍日期的格式化:(不要嫌多哦) JSTL格式化日期(本地化) 类似于数字和货币格式化,本地化环境还会影响生成日期和时间的方式. <%@ page pageEncoding="UT ...
- scp在Linux主机之间复制不用输入密码
把你的本地主机用户的ssh公匙文件复制到远程主机用户的~/.ssh/authorized_keys文件中,假设本地主机linux(10.1.1.1),远程主机linux(10.1.1.2) 一,在li ...
- iOS在线音乐播放SZKAVPlayer(基于AVPlayer的封装)
由于最近闲着没事,想找有关在线音乐播放的demo学习一下,在gitHub跟code4APP上面查找了很多帖子,结果很多在线音乐都是基于AudioStream实现的,我感觉用起来不太方便.后来突然发现, ...
- php ++a和a++
<?php$a=$b=5;$a+$b=$a++-++$b;echo $b;?> 输出-1
- ubuntu下搭建cocos2dx编程环境-中
上篇文章里讲了在ubuntu下部署cocos2d-x开发环境,这篇文章主要示范在ubuntu下部署cocos2d-x android开发环境.分开写就是因为我看很多文章里都将这两件事情混杂着写 ...
- 修改图层的symbol(AE+C#)
取出一个图层的symbol 在其基础上对其进行修改 private void button1_Click(object sender, EventArgs e) { mp;nbsp; ...