赞
踩
外包公司,为了节约成本,采用Android内嵌H5模式开发,便于在安卓移动端上直接复用页面, 从而提高开发效率。同时改动时候只需改动H5页面,无需改动客户端。
实现的原理:
本质是:Java代码和Javascript调用
H5页面,只是Html的扩展,Javascript用来处理页面的逻辑
Android和H5互调案例:
一.java调用js
1、Java 调用 js 里面的函数、效率并不是很高、估计要200ms左右吧、做交互性很强的事情、这种速度很难让人接受、而js去调Java的方法:速度很快、50ms左右、所以尽量用js调用Java方法
2、Java 调用 js 的函数、没有返回值、调用了就控制不到了
3、Js 调用 Java 的方法、返回值如果是字符串、你会发现这个字符串是 native奶踢吾 的、转成 locale老K乐 的才能正常使用、使用 toLocale
String() 函数就可以了、不过这个函数的速度并不快、转化的字符串如果很多、将会很耗费时间
4、网页中尽量不要使用jQuery、执行起来需要5-6秒、最好使用原生的js写业务脚本、以提升加载速度、改善用户体验
交互的话,安卓这边只需要配置webview的jsBridge就可以了
一般接口什么的都是h5那边提供,这边这需要根据提供的接口进行实现就可以了
数据交互也是这样的,还有一种是写cookie
对于安卓开发有一段时间的人来说,知道安卓4.4以前谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户
端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,增加了防御措施,如果要是js调用本地
代码,开发者必须在代码申明JavascriptInterface, 列如在4.0之前我们要使得webView加载js只需如下代码:
mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");
4.4之后调用需要在调用方法加入加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这
样就可以避免恶意网页利用js对安卓客户端的窃取和攻击。
但是即使这样,我们很多时候需要在js记载本地代码的时候,要做一些判断和限制,或者有可能也会做些过滤和对用户友好
提示,因此JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。
通过实例化webView,用法和安卓原生的view没多大区别,设置WebChromClient, 设置加载的html(同样支持网络和本地文件)
,接着我们需要给web注册和html端约定好的js方法名。代码列举的submitFromweb和js的执行的方法名一致,玩过NDK的JNI调
用的朋友也知道必须和c代码之间有个约定,其实js桥和jni有点类似,
通过注册handler来实现回调,Java代码中通过js返回的数据,进行处理后在调用function.onCallback返回给js.这里不做过多解释
//必须和js同名函数,注册具体执行函数,类似java实现类
拦截URL
Android中处理网页时我们必然用到WebView,这里我们有这样一个需求,我们想让WebView在处理网络请求的时候将某
些请求拦截替换成某些特殊的资源。具体一点儿说,在WebView加载 http://m.sogou.com 时,会加载一个logo图片,
我们的需求就是将这个logo图片换成另一张图片。
shouldInterceptRequest
好在Android中的WebView比较强大,从API 11(Android 3.0)开始, shouldInterceptRequest被引入就是为了解决这一类的问题。
shouldInterceptRequest这个回调可以通知主程序WebView处理的资源(css,js,image等)请求,并允许主程序进行处理后返回数据。如果主程序返回的数据为null,WebView会自行请求网络加载资源,否则使用主程序提供的数据。注意这个回调发生在非UI线程中,所以进行UI系统相关的操作是不可以的。shouldInterceptRequest有两种重载。
下面是:
WebView的基本设置:
private void initWebView() {
webView = new WebView(this);
WebSettings webSettings = webView.getSettings();
//设置支持javaScript脚步语言
webSettings.setJavaScriptEnabled(true);
//支持双击-前提是页面要支持才显示
//webSettings.setUseWideViewPort(true);
//支持缩放按钮-前提是页面要支持才显示
webSettings.setBuiltInZoomControls(true);
//设置客户端-不跳转到默认浏览器中
webView.setWebViewClient(new WebViewClient());
//设置支持js调用java
webView.addJavascriptInterface(new AndroidAndJSInterface(),"Android");
//加载本地资源
webView.loadUrl("file:///android_asset/JavaAndJavaScriptCall.html");
//显示页面
// setContentView(webView);
}
java调用js原理就是Java代码调用了Js里面的函数。
核心Java代码段:
//登录功能里,java代码调用了js里面的JavaCallJs函数实现将name传到JS中,这样JS页面可以显示该用户名了。
private void login(String name) {
webView.loadUrl("javascript:javaCallJs(" + "'" + name + "'" + ")");
setContentView(webView);
}
核心js代码段:
//上面Java核心代码执行将调用下面JS代码
<script type="text/javascript">
function javaCallJs(arg){
document.getElementById("content").innerHTML =
("欢迎:"+arg );
}
</script>
不过要实现H5在APP上进行操作,我们就需要重写WebChromeClient中的openFileChooser方法才行,顺便百度了下WebView中 WebViewClient与WebChromeClient的区别:
WebViewClient主要帮助WebView处理各种通知、请求事件的,比如:
onLoadResource |
onPageStart |
onPageFinish |
onReceiveError |
onReceivedHttpAuthRequest |
WebChromeClient主要辅助WebView处理JavaScript的对话框、网站图标、网站title、加载进度等比如
onCloseWindow(关闭WebView) |
onCreateWindow() |
onJsAlert (WebView上alert无效,需要定制WebChromeClient处理弹出) |
onJsPrompt |
onJsConfirm |
onProgressChanged |
onReceivedIcon |
onReceivedTitle |
看上去他们有很多不同,实际使用的话,如果你的WebView只是用来处理一些html的页面内容,只用WebViewClient就行了,如果需要更丰富的处理效果,比如JS、进度条等,就要用到WebChromeClient,所以我们用到了WebChromeClient。
举个上传的例子 得益于这两篇文章的帮助 :android使用webview上传文件(支持相册和拍照)_千里之外-CSDN博客_webview上传文件和Android 如何選取圖片或是檔案? | MagicLen,后来通过一些处理 ,最终我们的代码如下:
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String mTitle) {
super.onReceivedTitle(view, mTitle);
//这里可以用来处理我们的页面标题
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
//处理网页面加载进度
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> filePathCallback) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_SINGLE);
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback filePathCallback, String acceptType) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
}
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
}
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
mFilePathCallback5 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
return true;
}
});
我们这里选择好图片后,还要进行处理:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == IntentUtils.REQUEST_IMAGE) {
if (mFilePathCallback4 != null) {
if (null != intent) {
ArrayList<String> resultList = intent.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT);
if (resultList.size() > 0) {
String path = MediaUtility.getPath(BaseWebViewAct.this,
Uri.parse("file://" + resultList.get(0)));//
Uri uri = Uri.fromFile(new File(path));
mFilePathCallback4.onReceiveValue(uri);
}
}else {
mFilePathCallback4.onReceiveValue(null);
}
}
if (mFilePathCallback5 != null) {
if (null != intent) {
ArrayList<String> resultList =intent.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT);
if (resultList.size() > 0) {
String path = MediaUtility.getPath(BaseWebViewAct.this,
Uri.parse("file://" + resultList.get(0)));//
Uri uri = Uri.fromFile(new File(path));
mFilePathCallback5.onReceiveValue(new Uri[]{uri});
}
}else {
mFilePathCallback5.onReceiveValue(null);
}
}
mFilePathCallback4 = null;
mFilePathCallback5 = null;
}
}
二.JavaScript调java
1.在初始化webview代码中配置Javascript接口:
//设置支持js调用java,调用时候将执行第一个参数的接口类
webView.addJavascriptInterface(new AndroidAndJSInterface(),"Android");
//第二个参数为执行接口类方法的标示,与js相呼应
1
2
3
2.在该Activity中实现Javascript接口类:
/**
* js可以调用该类的方法
*/
class AndroidAndJSInterface{
@JavascriptInterface
public void showToast(){
Toast.makeText(JavaAndJSActivity.this, "我被js调用了", Toast.LENGTH_SHORT).show();
}
}
3_JavaScript中调用java代码的核心代码段:
<input type="button" value="点击Android被调用" οnclick="window.Android.showToast()" />
1
2
执行流程:
点击js页面的button,将执行js的onclick方法(οnclick=”window.Android.showToast()”),根据该Android标示与webview配置接口方法的第二个参数相匹配,然后执行js接口实现类的showToast()方法。从而实现js代码调用java代码。
H5页面调用Android播放视频
基本思路和上面的例子(执行流程)一样,业务逻辑层面来说播放视频主要是从H5页面点击播放跳转(把视频播放的url传递给Java层)并触发Java实现播放视频的代码即可。
1. webview中的核心配置:
//设置支持js调用java
webView.addJavascriptInterface(new AndroidAndJSInterface(),"android");
2.在该Activity中实现Javascript接口类:
class AndroidAndJSInterface {
/**
* 该方法将被js调用
* @param id
* @param videoUrl
* @param tile
*/
@JavascriptInterface
public void playVideo(int id,String videoUrl,String tile){
//调起系统所有播放器
Intent intent = new Intent();
intent.setDataAndType(Uri.parse(videoUrl),"video/*");
startActivity(intent);
}
}
3.JavaScript中调用java代码的核心代码段:
javascript:android.playVideo(itemid, videourl, itemtitle);
1
H5页面调用Android拨打电话
思路都是一样的,点击H5页面把电话号码传到java层并调用拨号核心代码即可。
在该Activity中实现Javascript接口类:
@JavascriptInterface
public void call(String phone) {
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phone));
if (ActivityCompat.checkSelfPermission(JsCallJavaCallPhoneActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(intent);
}
重点内容
1.如果调用如下方法时候报错,那么可以在Javascript接口类的具体实现方法上面加注解:@JavascriptInterface,或者把targetSdkVersion 改为 16(一般不适用)
//设置支持js调用java
webView.addJavascriptInterface(new AndroidAndJSInterface(), "Android");
1
2
2.该申请的权限不要忘了在AndroidManifest.xml中加上。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。