转载请标明出处:http://blog.csdn.net/jj583500596/article/details/69250664
上一篇WebView:基本设置与使用,了解了WebView的常用API、基本设置以及加载HTML网页。这一篇道长深入说一些常用的WebView使用方法。比如标题上写的拦截URL、JS交互、使用本地图片资源等。好了,废话就到这里,走起……
一、Android代码与JS交互
现在我们已经使用WebView加载好了HTML网页,我们可以点击网页上的控件进行操作,但是如果HTML中的一些控件需要与Android客户端相互传递参数或者调用一些Android客户端控件时我们要怎么做?答案就是使用JS交互来实现。
Android代码与JS交互的大前提:
// 启用WebView对JavaScript的支持
mWebView.getSettings().setJavaScriptEnabled(true);
1.Android代码调用JS
WebView调用JS代码非常简单,直接调用loadUrl,加载JS方法即可:
// 无参数调用
mWebView.loadUrl("javascript:javacalljs()");
// Java调用JS并传递参数
String content = "hello js, form Android code!";
mWebView.loadUrl("javascript:javacalljswithargs('" + content + "')");
// mWebView.loadUrl("javascript:setPhoto('" + filePath + "','" + filename + "')");
HTML页面中的JS代码:
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript">
function javacalljs(){
document.getElementById("content").innerHTML +=
"<br\>java调用了js函数";
}
function javacalljswithargs(arg){
document.getElementById("content").innerHTML +=
("<br\>"+arg);
}
</script>
</head>
2.JS调用Android代码
实现一个class,并在其中添加对应的方法,供JS调用:
/**
* Js调用的JavascriptInterface
*/
public class TestInterface {
/**
* 因为安全问题,在Android 4.2以后(如果应用的android:targetSdkVersion数值为17+)
* JS只能访问带有 @JavascriptInterface注解的Java函数。
*/
@JavascriptInterface
public void startCall() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + 10086));
startActivity(intent);
}
@JavascriptInterface
public void showToast(String content) {
Toast.makeText(MainActivity.this, "js调用了java函数并传递了参数:" + content, Toast.LENGTH_SHORT).show();
}
}
调用WebView的addJavascriptInterface方法,设置JS回调的对象
// 设置JavascriptInterface
// javainterface实际就是一个普通的java类,里面是我们本地实现的java代码
// 将object 传递给webview,并指定别名,这样js脚本就可以通过我们给的这个别名来调用我们的方法
// 在代码中,TestInterface是实例化的对象,testInterface是这个对象在js中的别名
mWebView.addJavascriptInterface(new TestInterface(), "testInterface");
HTML页面中的JS代码,回调Android代码的方式为:window.别名.方法名
<a onClick="window.testInterface.startCall()">点击拨打10086</a><br/>
<a onClick="window.testInterface.showToast('我弹了一个Toast')" >点击弹出Toast,内容为“我弹了一个Toast”</a>
前面有一个安全问题:与JS交互时,JS中夹如果杂有恶意代码(比如获取手机内存信息),Android 4.2以后做了优化,JS只能访问带有 @JavascriptInterface注解的Java函数,但是Android 4.2以前怎么解决?现在发现了两种解决方法,以后发现了新的解决方法道长会更新博客。
方法一:其实很简单,就是在你的jscalljava回调函数内另外开个线程去load JS代码即可:
mWebView.post(new Runnable() {
@Override
public void run() {
wb.loadUrl("javascript:display_alert()");
}
});
方法二:JS注入本地文件
为HTML注入JS文件,通过在webview的onPageFinished方法中执行JS代码注入:
第一种注入方式:
当webview加载完之后,读取整个JS文件中的内容,然后将整个文件内容以字符串的形式,通过webview.loadUrl(“javascript:fileContentString”)注入
URL url = new URL("http://www.rayray.ray/ray.js");
in = url.openStream();
byte buff[] = new byte[1024];
ByteArrayOutputStream fromFile = new ByteArrayOutputStream();
FileOutputStream out = null;
do {
int numread = in.read(buff);
if (numread <= 0) {
break;
}
fromFile.write(buff, 0, numread);
} while (true);
String wholeJS = fromFile.toString();
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webview.loadUrl("javascript:" + wholeJS);
}
第二种注入方式:
页面加载完之后,直接向webview对应的html中加入”script”便签,并包含要注入的js的Url地址,如下:
String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"http://www.123.456/789.js\";";
js += "document.body.appendChild(newscript);";
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webview.loadUrl("javascript:" + js);
}
二、拦截URL
其实拦截url也可以实现与Android客户端交互,但是拦截url不能实现与Android客户端相互传参,只是在一些跳转时用的比较多
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 当url里面包含article字段的时候,则跳转到TwoStairsActivity页面,否则还是继续显示网页
// 返回值:true 不会显示网页资源,需要等待你的处理,false 就认为系统没有做处理,会显示网页资源
if (!TextUtils.isEmpty(url) && url.contains("article")) {
Intent intent = new Intent(ClubActivityDetailActivity.this, TwoStairsActivity.class);
intent.putExtra("url", url);
startActivity(intent);
return true;
}
return false;
}
});
三、加载Html文件时使用本地图片资源
加载HTML页面的时候,有的App客户端会选择把图片资源放在本地,以减少加载资源比较多的情况下导致的该页面加载缓慢,耗费大量的流量。这对流量限制的客户端来说,用户体验非常不好。所以将这些图片资源存放在app中,加载HTML时直接从本地加载这些图片资源。(只针对HTML文件从网络上获取)
方法一:转换成Base64编码
- 将图片文件转换成Base64编码
import com.parse.codec.binary.Base64;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 将图片转换为Base64<br>
* 将base64编码字符串解码成img图片
*
* @创建时间 2015-06-01 15:50
*/
public class Img2Base64Util {
public static void main(String[] args) {
String imgFile = "d:\\3.jpg";//待处理的图片
String imgbese = getImgStr(imgFile);
System.out.println(imgbese.length());
System.out.println(imgbese);
String imgFilePath = "d:\\332.jpg";//新生成的图片
generateImage(imgbese, imgFilePath);
}
/**
* 将图片转换成Base64编码
*
* @param imgFile 待处理图片
* @return
*/
public static String getImgStr(String imgFile) {
//将图片文件转化为字节数组字符串,并对其进行Base64编码处理
InputStream in = null;
byte[] data = null;
//读取图片字节数组
try {
in = new FileInputStream(imgFile);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return new String(Base64.encodeBase64(data));
}
/**
* 对字节数组字符串进行Base64解码并生成图片
*
* @param imgStr 图片数据
* @param imgFilePath 保存图片全路径地址
* @return
*/
public static boolean generateImage(String imgStr, String imgFilePath) {
//
if (imgStr == null) //图像数据为空
return false;
try {
//Base64解码
byte[] b = Base64.decodeBase64(imgStr);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {//调整异常数据
b[i] += 256;
}
}
//生成jpeg图片
OutputStream out = new FileOutputStream(imgFilePath);
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
return false;
}
}
}
- JS代码,将当前的fileBase64传到HTML页面中,并设置到img上
<img alt="Embedded Image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." />
注意:
在上面的URL中,data表示取得数据的协定名称,image/png 是数据类型名称,base64 是数据的编码方法,逗号后面就是这个image/png文件base64编码后的数据。
目前,url scheme支持的类型有:
data: - 文本数据
data:text/plain - 文本数据
data:text/html - HTML代码
data:text/html;base64 - base64编码的HTML代码
data:text/css - CSS代码
data:text/css;base64 - base64编码的CSS代码
data:text/javascript - Javascript代码
data:text/javascript;base64 - base64编码的Javascript代码
data:image/gif;base64 - base64编码的gif图片数据
data:image/png;base64 - base64编码的png图片数据
data:image/jpeg;base64 - base64编码的jpeg图片数据
data:image/x-icon;base6 - base64编码的icon图片数据
所以在转换完成以后要在Base64字符串前面拼接上相应的头,如果不拼接的话浏览器会不识别字符串。
优缺点:转换成Base64编码这种方法适合小图片,如果图片过大的话重新编码也会耗费大量时间。
方法二:使用shouldInterceptRequest拦截
shouldInterceptRequest这个回调可以通知主程序WebView处理的资源(css/js/image等)请求并允许主程序进行处理后返回数据。如果主程序返回的数据为null则WebView会自行请求网络加载资源,否则使用主程序提供的数据。注意:这个回调发生在非UI线程中,所以不可以进行UI相关的操作。
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
从API 11开始引入,API 21弃用
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
从API 21开始引入,所以在使用的时候,最好两者都要进行重写。
mWebView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
String key = "http://localhost/";
WebResourceResponse response = null;
if (url.contains(key)) {
try {
String imgPath = url.replace(key,"");
imgPath = Uri.parse(imgPath).getPath();
InputStream localCopy = new FileInputStream(imgPath);
//当前只针对图片
response = new WebResourceResponse("image/png", "UTF-8", localCopy);
} catch (IOException e) {
e.printStackTrace();
}
}
return response;
}
});
当从本地相册或者拍照完成后,图片的URL可能是这样的/storage/emulated/0/newCrop.jpg,如果这个URL回调给JS是显示不出来的。因为WebView根本不会识别并自动加载。
需要把图片路径拼接成如下样式:
filePath = "http://localhost/file:///storage/emulated/0/newCrop.jpg"
通过Android调用JS代码设置图片
final String filename = new File(filePath).getName();
mHandler.post(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:setPhoto('" + filePath + "','" + filename + "')");
}
});
/**
* JS回调Java代码
*/
public class JsInteration {
@JavascriptInterface
public void setPhoto(String filePath, String fileName) {
// Toast.makeText(ClubApplyActivity.this, filePath, Toast.LENGTH_SHORT).show();
}
}
JS代码:
<img src="http://localhost/file:///storage/emulated/0/newCrop.jpg" alt="" class="img-responsive img-thumbnail">
好了,咱们的WebView使用暂时介绍到这里,以后如果还有补充,道长会继续更新。希望这篇博客能为你提供一些帮助。