文章大纲
- 引言
- 一、WebView 概述
- 二、WebView的常用的方法
- 三、WebView的应用
- 四、通过WebView实现Java和Javascript的互相交互步骤
- 五、WebView的缓存机制
- 六、使用WebView时注意事项
- 1、加载本地assets里的js,css,html资源时候,在编写html文件引用这些资源需要把对应在assets下的**绝对路径**引入
- 2、 使用loadData(data)方法加载并显示网页时候,data里含有以下四个特殊字符:'#', '%', '\' , ' ' ,如果有的话可以用 %23, %25, %27, %3f来替换,解决中文乱码可以传递编码——utf-8来解决否则可能造成错误。
- 3、加载有特殊字符的网页时,可以考虑使用loadDataWithBaseURL方法
- 4、加载静态网页没有问题,加载远程(WIFI)动态页面时需要注意,图片加载可能会出现问题(因为一般情况下Web页面中给出的是图片相对路径,并没有给完整的HTPP路径,所以有时候得自行处理下,不然图片无法正常加载)
引言
目前很多android app都内置了可以显示web页面的界面,会发现这个界面一般都是由一个叫做WebView的组件渲染出来的,学习该组件可以为你的app开发提升扩展性。Java和Javascript交互项目实战源码
一、WebView 概述
正如字面意思一样,是用来展示Web页面的。通过WebView控件我们可以在App中直接使用PC端的Web页面,通过addJavascriptInterface方法还可以实现JavaScript方法和Java方法的互相调用,减少了不少的开发成本。简而言之,WebView就是内置小型浏览器。
二、WebView的常用的方法
1、构造方法
方法名 | 参数说明 |
---|---|
WebView(Context context) | 传递上下文用于访问应用程序资源 |
WebView(Context context, AttributeSet attrs) | attrs:传递给父容器的属性集合 |
WebView(Context context, AttributeSet attrs, int defStyle) | defStyle:样式style资源的id |
2、其他常用方法
方法名 | 参数说明 | 用法说明 |
---|---|---|
void addJavascriptInterface(Object obj, String interfaceName) | obj:自定义的Java对象,interfaceName:js中调用时使用的实例变量名 | 将对象绑定到的Javascript,这样的方法可以从JavaScript访问。 |
boolean canGoBack()/canGoBackOrForward()/canGoForward() | 设置是否允许返回上一页、前进等 | |
boolean dispatchKeyEvent(KeyEvent event) | event对象 | 设置事件的传递 |
String getOriginalUrl() | 获取原始的Url | |
int getProgress() | 获取当前网页的进度 | |
WebSettings getSettings() | 获取WebView一系列属性集合,用于配置WebView | |
void loadData(String data, String mimeType, String encoding) | data:字符串拼接形式的html代码 | 用于把字符串形式的html代码渲染成为网页的形式 |
void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) | ||
void loadUrl(String url) | 把url对应的文件或者url网址渲染成为网页 | |
void setWebChromeClient(WebChromeClient client) | 设置打开i时不用系统浏览器,而使用本地WebView打开 |
3、WebSettings常用的方法
方法名 | 用法说明 |
---|---|
setSupportZoom(boolean support) | 设置WebView是否支持使用屏幕控件或手势进行缩放,默认是true,支持缩放。 |
setMediaPlaybackRequiresUserGesture(boolean require) | 设置WebView是否通过手势触发播放媒体,默认是true,需要手势触发。 |
setBuiltInZoomControls(boolean enabled) | 设置WebView是否使用其内置的变焦机制,该机制集合屏幕缩放控件使用,默认是false,不使用内置变焦机制。 |
setDisplayZoomControls(boolean enabled) | 设置WebView使用内置缩放机制时,是否展现在屏幕缩放控件上,默认true,展现在控件上。 |
setAllowFileAccess(boolean allow) | 设置在WebView内部是否允许访问文件,默认允许访问。 |
setAllowContentAccess(boolean allow) | 设置WebView是否使用其内置的变焦机制,该机制结合屏幕缩放控件使用,默认是false,不使用内置变焦机制。 |
setLoadWithOverviewMode(boolean overview) | 设置WebView是否使用预览模式加载界面。 |
setSaveFormData(boolean save) | 设置WebView是否保存表单数据,默认true,保存数据。 |
setTextZoom(int textZoom) | 设置WebView中加载页面字体变焦百分比,默认100,整型数。 |
setAcceptThirdPartyCookies(boolean accept) | 设置WebView访问第三方Cookies策略,参考CookieManager提供的方法:setShouldAcceptThirdPartyCookies |
setUseWideViewPort(boolean use) | 设置WebView是否使用viewport,当该属性被设置为false时,加载页面的宽度总是适应WebView控件宽度;当被设置为true,当前页面包含viewport属性标签,在标签中指定宽度值生效,如果页面不包含viewport标签,无法提供一个宽度值,这个时候该方法将被使用,当setUseWideViewPort(true)和setLoadWithOverviewMode(true)可以支持自适应 |
setSupportMultipleWindows(boolean support) | 设置WebView是否支持多屏窗口,参考WebChromeClient#onCreateWindow,默认false,不支持。 |
setLayoutAlgorithm(LayoutAlgorithm l) | 设置WebView底层的布局算法,参考LayoutAlgorithm#NARROW_COLUMNS,将会重新生成WebView布局 |
setStandardFontFamily(String font) | 设置WebView标准字体库字体,默认字体“sans-serif”。 |
setFixedFontFamily(String font) | 设置WebView固定的字体库字体,默认“monospace”。 |
setSansSerifFontFamily(String font) | 设置WebView Sans SeriFontFamily字体库字体,默认“sans-serif”。 |
setSerifFontFamily(String font) | 设置WebView seri FontFamily字体库字体,默认“sans-serif”。 |
setCursiveFontFamily(String font) | 设置WebView字体库字体,默认“cursive” |
setFantasyFontFamily(String font) | 设置WebView字体库字体,默认“fantasy”。 |
setMinimumFontSize(int size) | 设置WebView字体最小值,默认值8,取值1到72 |
setMinimumLogicalFontSize(int size) | 设置WebView逻辑上最小字体值,默认值8,取值1到72 |
setDefaultFontSize(int size) | 设置WebView默认值字体值,默认值16,取值1到72 |
setDefaultFixedFontSize(int size) | 设置WebView默认固定的字体值,默认值16,取值1到72 |
setLoadsImagesAutomatically(boolean flag) | 设置WebView是否加载图片资源,默认true,自动加载图片 |
setBlockNetworkImage(boolean flag) | 设置WebView是否以http |
setBlockNetworkLoads(boolean flag) | 设置WebView是否从网络加载资源,Application需要设置访问网络权限,否则报异常 |
setJavaScriptEnabled(boolean flag) | 设置WebView是否允许执行JavaScript脚本,默认false,不允许 |
setAllowUniversalAccessFromFileURLs(boolean flag) | 设置WebView运行中的脚本可以是否访问任何原始起点内容,默认true |
setAllowFileAccessFromFileURLs(boolean flag) | 设置WebView运行中的一个文件方案被允许访问其他文件方案中的内容,默认值true |
setGeolocationDatabasePath(String databasePath) | 设置WebView保存地理位置信息数据路径,指定的路径Application具备写入权限 |
setAppCacheEnabled(boolean flag) | 设置Application缓存API是否开启,默认false,设置有效的缓存路径参考setAppCachePath(String path)方法 |
setAppCachePath(String appCachePath) | 设置当前Application缓存文件路径,Application Cache API能够开启需要指定Application具备写入权限的路径 |
setDatabaseEnabled(boolean flag) | 设置是否开启数据库存储API权限,默认false,未开启,可以参考setDatabasePath(String path) |
setDomStorageEnabled(boolean flag) | 设置是否开启DOM存储API权限,默认false,未开启,设置为true,WebView能够使用DOM storage API |
setGeolocationEnabled(boolean flag) | 设置是否开启定位功能,默认true,开启定位 |
setJavaScriptCanOpenWindowsAutomatically(boolean flag) | 设置脚本是否允许自动打开弹窗,默认false,不允许 |
setDefaultTextEncodingName(String encoding) | 设置WebView加载页面文本内容的编码,默认“UTF-8”。 |
setUserAgentString(String ua) | 设置WebView代理字符串,如果String为null或为空,将使用系统默认值 |
setNeedInitialFocus(boolean flag) | 设置WebView是否需要设置一个节点获取焦点当被回调的时候,默认true |
setCacheMode(int mode) | 重写缓存被使用到的方法,该方法基于Navigation Type,加载普通的页面,将会检查缓存同时重新验证是否需要加载,如果不需要重新加载,将直接从缓存读取数据,允许客户端通过指定LOAD_DEFAULT |
setMixedContentMode(int mode) | 设置当一个安全站点企图加载来自一个不安全站点资源时WebView的行为,android.os.Build.VERSION_CODES.KITKAT默认为MIXED_CONTENT_ALWAYS_ALLOW,android.os.Build.VERSION_CODES#LOLLIPOP默认为MIXED_CONTENT_NEVER_ALLOW,取值其中之一:MIXED_CONTENT_NEVER_ALLOW |
三、WebView的应用
1、使用WebView的基本步骤
- 首先,构造WebView,可以通过静态构造直接在xml布局里声明,还可以通过java代码动态创建。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<WebView android:id="@+id/id_table_webview" android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
mWebview = (WebView) findViewById(R.id.id_table_webview);//获取WebVIew
WebView webView = new WebView(context);
- 其次,在Activity的生命周期方法(一般在onCreate)里,配置WebView(主要是先调用getSetting()获取属性集再设置、设置支持javascript、回退、是否使用系统自带浏览器打开等等)
public class MyWebViewCient extends WebViewClient {
/**
* WebView能够响应页面的超链接
* @param view
* @param url
* @return
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
mWebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);///一系列的初始化设置
mWebview.getSettings().setAllowFileAccess(true);// 设置允许访问文件数据
mWebview.getSettings().setSupportZoom(true);//支持放大网页功能
mWebview.getSettings().setBuiltInZoomControls(true);//支持缩小网页功能
mWebview.getSettings().setJavaScriptEnabled(true);
mWebview.addJavascriptInterface(new JSObject(), "employee");//前面对象,后面js中的调用名(我们可以看成这个JSObject类的实例是employee,用于给javascript里调用
mWebview.setWebViewClient(new MyWebViewCient());//设置打开i时不用系统浏览器。使用本地WebView打开
- 然后调用loadUrl、loadData、loadDataWithBaseURL加载并显示对应的网页内容
mWebView.loadUrl("http://www.google.com"); //加载互联网的url地址
mWebView.loadUrl("file:///android_asset/xxx.html"); //加载并显示存放在assets 文件下的html
mWebView.loadUrl("file:///mnt/sdcard/web/xxx.html" );//加载sdcard下的html
//设置回退,覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebview.canGoBack()) {
mWebview.goBack(); //返回WebView的上一页面
return true;
}
return false;
}
- 接着,若用WebView点链接跳转到其他页以后,为了让WebView支持回退功能,则需要覆盖Activity类的onKeyDown()方法;反之,不做任何处理,点击系统回退剪键,整个浏览器会调用finish()而结束自身,而不是回退到上一页面
5.最后,还需在AndroidManifest.xml文件中添加权限——uses-permission android:name=“android.permission.INTERNET”,否则会出现Web page not available错误。
package com.xiaoi.app.smartbot.view.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.xiaoi.app.smartbot.R;
/**
* auther: Crazy.Mo
* Date: 2017/4/5
* Time:15:08
* Des:
*/
public class WebViewActivity extends BaseActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
init();
}
private void init(){
webView= (WebView) findViewById(R.id.id_webv);
initWebView();
}
private void initWebView(){
if(getIntent()!=null) {
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);///一系列的初始化设置
webView.getSettings().setAllowFileAccess(true);// 设置允许访问文件数据
webView.getSettings().setSupportZoom(true);//支持放大网页功能
webView.getSettings().setBuiltInZoomControls(true);//支持缩小网页功能
webView.getSettings().setJavaScriptEnabled(true);
///webView.addJavascriptInterface(new JSObject(), "employee");//前面对象,后面js中的调用名(我们可以看成这个JSObject类的实例是employee,用于给javascript里调用
webView.loadUrl(getIntent().getStringExtra("url"));
webView.setWebViewClient(new MyWebViewCient());//设置打开i时不用系统浏览器。使用本地WebView打开
}
}
public static void openWebViewActivity(@NonNull Context context,@NonNull int requestCode,@NonNull String url){
WebViewActivity activity=new WebViewActivity();
Intent intent=new Intent(context,WebViewActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
///activity.startActivityForResult(intent,requestCode);
}
//设置回退,覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法
// @Override
// public boolean onKeyDown(int keyCode, KeyEvent event) {
// if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
// webView.goBack(); //返回WebView的上一页面
// return true;
// }
// return false;
// }
private static class MyWebViewCient extends WebViewClient {
/**
* WebView能够响应页面的超链接
* @param view
* @param url
* @return
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
webView.loadUrl("about:blank");
webView.stopLoading();
webView= null;
}
四、通过WebView实现Java和Javascript的互相交互步骤
Android端是不能直接去和Javascript交互的,反过来也一样。WebView实现Java和Javascript的互相交互实例
1、Java端通过WebView调用Javascript脚本方法
-
获取WebView对象并设置setJavaScriptEnabled(true)及其他初始化
-
通过WebView实例的loadUrl方法调用javascript脚本方法,通用语法格式mWebview.loadUrl(“javascript: 网页中对应的Javascript的方法名([参数])”);//通过webview调用js 方法,需要注意的是参数类型必须一致,如果不一致需要转为为统一的数据格式
2、Javascript端调用Java方法
-
获取WebView对象并设置setJavaScriptEnabled(true)及其他初始化
-
在Java端自定义提供给Javascript端调用的公共方法showContacts并封装为Java对象,建议以JSXxxxx来命名这个对象,比如JSOject
-
通过WebView实例的addJavascriptInterface(new JSXxxxx(),“employee”),添加上javascritp端对应的实例名employee
-
在Javascript端通过javascript:employee.showContacts()间接调用Java方法showContacts
private final class JSObject {
/**
* html中通过自定义的js 对象调用
* 高能预警:If you've set your targetSdkVersion to 17 or higher, you must add the @JavascriptInterface
即在一切需要在JS中调用的对象方法前加上@JavascriptInterface, 在api 17 即 Android 4.2.2 之后
*/
@JavascriptInterface
public void showContacts() {
List<Contacts> contactses = mContactsService.getContactsImf();
JSONArray jsonArray = new JSONArray();
try {
for (Contacts contact : contactses) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", contact.getId());
jsonObject.put("name", contact.getName());
jsonObject.put("phone", contact.getPhone());
jsonArray.put(jsonObject);
}
} catch (JSONException e) {
e.printStackTrace();
}
final String json = jsonArray.toString();
/**
* A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread
* 简单来说就是html里调用java方法和java调用js方法必须在同一线程
*/
mWebview.post(new Runnable() {
@Override
public void run() {
mWebview.loadUrl("javascript:show('" + json + "')");//通过webview调用js 方法
}
});
}
}
// Javascript端调用
<body onload="javascript:employee.showContacts()">
五、WebView的缓存机制
WebView是手机中内置了一款高性能webkit 内核浏览器,在SDK 中封装的一个组件,在Android端中缓存可以分为:网页缓存和应用缓存。
1、网页缓存
网页缓存在data/应用package下生成database与cache两个文件夹,请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下
/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db
支持的缓存模式有:
- LOAD_CACHE_ONLY—— 不使用网络,只读取本地缓存数据,
- LOAD_DEFAULT——根据cache-control决定是否从网络上取数据,
- LOAD_CACHE_NORMAL——API level 17中已经废弃, 从API level 11开始作用同- - LOAD_DEFAULT模式,
- LOAD_NO_CACHE——不使用缓存,只从网络获取数据,
- LOAD_CACHE_ELSE_NETWORK——只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
如果一个页面的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。如果一个页面的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
2、应用缓存
根据setAppCachePath(String appCachePath)提供的路径,在H5使用缓存过程中生成的缓存文件。无模式选择,通过setAppCacheEnabled(boolean flag)设置是否打开。默认关闭,即,H5的缓存无法使用。如果要手动清理缓存,需要找到调用setAppCachePath(String appCachePath)设置缓存的路径,把它下面的文件全部删除就OK了。
六、使用WebView时注意事项
1、加载本地assets里的js,css,html资源时候,在编写html文件引用这些资源需要把对应在assets下的绝对路径引入
2、 使用loadData(data)方法加载并显示网页时候,data里含有以下四个特殊字符:’#’, ‘%’, ‘’ , ’ ’ ,如果有的话可以用 %23, %25, %27, %3f来替换,解决中文乱码可以传递编码——utf-8来解决否则可能造成错误。
%:会报找不到页面错误,页面全是乱码。
#:会让你的goBack失效,但canGoBack是可以使用的。于是就会产生返回按钮生效,但不能返回的情况。
\:在转换时,会报错,因为它会把\当作转义符来使用,如果用两级转义,也不生效
目前较好的转码方案
//目前较好的转码方案
final String digits = "0123456789ABCDEF";
public String encode(String s){
StringBuilder buf = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9') || ".-*_".indexOf(ch) > -1) { //$NON-NLS-1$
buf.append(ch);
} else {
byte[] bytes = new String(new char[] { ch }).getBytes();
for (int j = 0; j < bytes.length; j++) {
buf.append('%');
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
buf.append(digits.charAt(bytes[j] & 0xf));
}
}
}
return buf.toString();
}
mWebView.loadData(this.encode(html), "text/html", "utf-8");
值得注意的是,当我替换掉特殊字符再使用loadData时,如果有大量的特殊字符和大量stytle(会使用很多%号)会给运行速度带来很大的影响,页面的数据越多,运行的速度就会越慢。
3、加载有特殊字符的网页时,可以考虑使用loadDataWithBaseURL方法
4、加载静态网页没有问题,加载远程(WIFI)动态页面时需要注意,图片加载可能会出现问题(因为一般情况下Web页面中给出的是图片相对路径,并没有给完整的HTPP路径,所以有时候得自行处理下,不然图片无法正常加载)
待续…