void loadUrl(String url)
这个函数主要加载url所对应的网页地址,或者用于调用网页中的指定的JS方法(调用js方法的用法,后面会讲),但有一点必须注意的是:loadUrl()必须在主线程中执行!!!否则就会报错!!!。
注意:加载在线网页地址是会用到联网permission权限。
url = "http://www.baidu.com";
...
mLoad.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl(url);
}
});
代码很简单,就是在点击按钮的时候加载网址,但需要注意的是:网址必须完整即以http://或者ftp://等协议开头,不能省略!不然将加载不出来,这是因为webview是没有自动补全协议功能的,所以如果我们不加,它将识别不出来网址类型,也就加载不出来了。
但如果我们运行上面的代码,效果却是利用浏览器来打开网址,却不是使用webview打开网址:
如果我们想实现在webview中打开网址需要怎么做呢? 我们需要设置WebViewClient:
mWebView.setWebViewClient(new WebViewClient());
再来运行一下看看:
要在WebView中打开链接,就必须要设置WebViewClient。
本地html文件可以放在assets文件夹下,也可以放在手机目录下。
public static final String OFFLINE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/offline/";
url = "file:///android_asset/web.html"; //Assets目录下
url = "file://" + OFFLINE_PATH_DOC + "web.html"; //手机目录下
或者 url = "file:///storage/emulated/0/offline/web.html"
HTML内容:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<h1>WebView加载本地HTMLh1>
head>
<body>
body>
html>
对于加载URL的总结就是:
1、如果是在线网址记得添加网络访问权限
2、在线网址中,如果要使用webview打开,记得设置WebViewClient
3、打开本地html文件时,是不需要设置WebViewClient,对应的asstes目录的url为:file:///android_asset/xxxxx
上面讲了通过loadUrl()来加载本地页面和在线地址的方式,这里给大家再补充两个方法LoadData()与loadDataWithBaseURL(),它们不是用来加载整个页面文件的,而是用来加载一段代码片的。
1. LoadData()
public void loadData(String data, String mimeType, String encoding)
public class MyActivity extends Activity {
private WebView mWebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDefaultTextEncodingName("utf-8");
String summary = "A android developer.";
mWebView.loadData(summary, "text/html", "utf-8");
}
}
在使用loadData时,在数据里面不能出现英文字符:’#’, ‘%’, ‘\’ , ‘?’ 这四个字符,如果有的话可以用 %23, %25, %27, %3f,这些字符来替换。直接使用这四个字符会造成的问题如下:
%:会报找不到页面错误,页面全是乱码。
#:会让你的goBack失效,但canGoBAck是可以使用的。于是就会产生返回按钮生效,但不能返回的情况。
\ 和? :在转换时,会报错,因为它会把\当作转义符来使用,如果用两级转义,也不生效。
其实,Android给我们提供了一个专门用来转码的函数:URLEncoder.encode(String s, String charsetName) ,它能将冲突的字符进行转义,然后再传给webview,这样webview在加载时就不会有冲突了,encode函数的声明如下:
/**
String s:代码段
String charsetName:编码类型
*/
public static String encode(String s, String charsetName);
使用data中,如果出现中文乱码问题,解决办法:参数传”utf-8”,页面的编码格式也必须是utf-8,这样编码统一就不会乱了。
注意:
1、loadData()应该是不能加载图片的,加载图片的内容我们后面会使用loadDataWithBaseURL来实现。
2、为了防止字符冲突,在传递loadData的数据时,必须使用URLEncoder.encode()函数来转义
3、页面的编码格式必须与代码中传参的编码格式一致,不然会导致乱码
1. loadDataWithBaseURL()
相比loadData,这个函数更常用,因为loadData能实现的功能,它都能实现,而且也不会出现字符冲突。其函数声明如下:
public void loadDataWithBaseURL(String baseUrl, String data,String mimeType, String encoding, String historyUrl)
public class MyActivity extends Activity {
private WebView mWebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDefaultTextEncodingName("utf-8");
String baseURL = "https://img-my.csdn.net";
String data = "风景优美 ";
mWebView.loadDataWithBaseURL(baseURL, data, "text/html", "utf-8", null);
}
}
总结:
loadData和loadDataWithBaseURL这两种方法,我建议使用后者,虽然loadData的历史记录不需要我们自己来实现,但在使用时,这就两个加载上后者比前者快一到两倍,且不会出现字符冲突。另外loadData不能加载图片,而loadDataWithBaseURL是可以加载图片的。
如果我们需要设置WebView的属性,是通过WebView.getSettings()获取设置WebView的WebSettings对象, 然后调用WebSettings中的方法来实现的。
WebSettings的方法及说明如下:
/**
* 是否支持缩放,配合方法setBuiltInZoomControls使用,默认true
*/
setSupportZoom(boolean support)
/**
* 是否需要用户手势来播放Media,默认true
*/
setMediaPlaybackRequiresUserGesture(boolean require)
/**
* 是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false
*/
setBuiltInZoomControls(boolean enabled)
/**
* 是否显示窗口悬浮的缩放控制,默认true
*/
setDisplayZoomControls(boolean enabled)
/**
* 是否允许访问WebView内部文件,默认true
*/
setAllowFileAccess(boolean allow)
/**
* 是否允许获取WebView的内容URL ,可以让WebView访问ContentPrivider存储的内容。 默认true
*/
setAllowContentAccess(boolean allow)
/**
* 是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false
*/
setLoadWithOverviewMode(boolean overview)
/**
* 是否保存表单数据,默认false
*/
setSaveFormData(boolean save)
/**
* 设置页面文字缩放百分比,默认100%
*/
setTextZoom(int textZoom)
/**
* 是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口
*/
setUseWideViewPort(boolean use)
/**
* 是否支持多窗口,如果设置为true ,WebChromeClient#onCreateWindow方法必须被主程序实现,默认false
*/
setSupportMultipleWindows(boolean support)
/**
* 指定WebView的页面布局显示形式,调用该方法会引起页面重绘。默认LayoutAlgorithm#NARROW_COLUMNS
*/
setLayoutAlgorithm(LayoutAlgorithm l)
/**
* 设置标准的字体族,默认”sans-serif”。font-family 规定元素的字体系列。
* font-family 可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体,
* 则会尝试下一个。也就是说,font-family 属性的值是用于某个元素的字体族名称或/及类族名称的一个
* 优先表。浏览器会使用它可识别的第一个值。
*/
setStandardFontFamily(String font)
/**
* 设置混合字体族。默认”monospace”
*/
setFixedFontFamily(String font)
/**
* 设置SansSerif字体族。默认”sans-serif”
*/
setSansSerifFontFamily(String font)
/**
* 设置SerifFont字体族,默认”sans-serif”
*/
setSerifFontFamily(String font)
/**
* 设置CursiveFont字体族,默认”cursive”
*/
setCursiveFontFamily(String font)
/**
* 设置FantasyFont字体族,默认”fantasy”
*/
setFantasyFontFamily(String font)
/**
* 设置最小字体,默认8. 取值区间[1-72],超过范围,使用其上限值。
*/
setMinimumFontSize(int size)
/**
* 设置最小逻辑字体,默认8. 取值区间[1-72],超过范围,使用其上限值。
*/
setMinimumLogicalFontSize(int size)
/**
* 设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
*/
setDefaultFontSize(int size)
/**
* 设置默认填充字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
*/
setDefaultFixedFontSize(int size)
/**
* 设置是否加载图片资源,注意:方法控制所有的资源图片显示,包括嵌入的本地图片资源。
* 使用方法setBlockNetworkImage则只限制网络资源图片的显示。值设置为true后,
* webview会自动加载网络图片。默认true
*/
setLoadsImagesAutomatically(boolean flag)
/**
* 是否加载网络图片资源。注意如果getLoadsImagesAutomatically返回false,则该方法没有效果。
* 如果使用setBlockNetworkLoads设置为false,该方法设置为false,也不会显示网络图片。
* 当值从true改为false时。WebView会自动加载网络图片。
*/
setBlockNetworkImage(boolean flag)
/**
* 设置是否加载网络资源。注意如果值从true切换为false后,WebView不会自动加载,
* 除非调用WebView#reload().如果没有android.Manifest.permission#INTERNET权限,
* 值设为false,则会抛出java.lang.SecurityException异常。
* 默认值:有android.Manifest.permission#INTERNET权限时为false,其他为true。
*/
setBlockNetworkLoads(boolean flag)
/**
* 设置是否允许执行JS。
*/
setJavaScriptEnabled(boolean flag)
/**
* 是否允许Js访问任何来源的内容。包括访问file scheme的URLs。考虑到安全性,
* 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的,
* 不会受到影响。ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本
* 以上默认为false
*/
setAllowUniversalAccessFromFileURLs(boolean flag)
/**
* 是否允许Js访问其他file scheme的URLs。包括访问file scheme的资源。考虑到安全性,
* 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的,
* 不会受到影响。如果getAllowUniversalAccessFromFileURLs为true,则该方法被忽略。
* ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本以上默认为false
*/
setAllowFileAccessFromFileURLs(boolean flag)
/**
* 设置存储定位数据库的位置,考虑到位置权限和持久化Cache缓存,Application需要拥有指定路径的
* write权限
*/
setGeolocationDatabasePath(String databasePath)
/**
* 是否允许Cache,默认false。考虑需要存储缓存,应该为缓存指定存储路径setAppCachePath
*/
setAppCacheEnabled(boolean flag)
/**
* 设置Cache API缓存路径。为了保证可以访问Cache,Application需要拥有指定路径的write权限。
* 该方法应该只调用一次,多次调用自动忽略。
*/
setAppCachePath(String appCachePath)
/**
* 是否允许数据库存储。默认false。查看setDatabasePath API 如何正确设置数据库存储。
* 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView
* 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置
*/
setDatabaseEnabled(boolean flag)
/**
* 是否存储页面DOM结构,默认false。
*/
setDomStorageEnabled(boolean flag)
/**
* 是否允许定位,默认true。注意:为了保证定位可以使用,要保证以下几点:
* Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的权限
* Application 需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的回调,
* 接收Js定位请求访问地理位置的通知
*/
setGeolocationEnabled(boolean flag)
/**
* 是否允许JS自动打开窗口。默认false
*/
setJavaScriptCanOpenWindowsAutomatically(boolean flag)
/**
* 设置页面的编码格式,默认UTF-8
*/
setDefaultTextEncodingName(String encoding)
/**
* 设置WebView代理,默认使用默认值
*/
setUserAgentString(String ua)
/**
* 通知WebView是否需要设置一个节点获取焦点当
* WebView#requestFocus(int,android.graphics.Rect)被调用的时候,默认true
*/
setNeedInitialFocus(boolean flag)
/**
* 基于WebView导航的类型使用缓存:正常页面加载会加载缓存并按需判断内容是否需要重新验证。
* 如果是页面返回,页面内容不会重新加载,直接从缓存中恢复。setCacheMode允许客户端根据指定的模式来
* 使用缓存。
* LOAD_DEFAULT 默认加载方式
* LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存
* LOAD_NO_CACHE 不使用缓存
* LOAD_CACHE_ONLY 只使用缓存
*/
setCacheMode(int mode)
/**
* 设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW方
* 式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW
*/
setMixedContentMode(int mode)
例如:
示例1:打开页面时, 自适应屏幕:
webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放
webSettings.setLoadWithOverviewMode(true);
效果图如下:(所使用的网址为:http://www.w3school.com.cn/)
注意:自己写的网页代码,也可以在HTML中做宽度100%自适应屏幕
示例2:使页面支持缩放:
//开启javascript支持
webSettings.setJavaScriptEnabled(true);
// 设置可以支持缩放
webSettings.setSupportZoom(true);
// 设置出现缩放工具
webSettings.setBuiltInZoomControls(true);
示例3:如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点
webview.requestFocusFromTouch();
其他请自行摸索。
WebView是Android中直接加载html页面的控件。当我们加载Html时候,会在我们data/应用package下生成database与cache两个文件夹:
我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下。
WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)、H5缓存(即AppCache)。
网页缓存的结构:
/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db
缓存模式(5种)
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,都使用缓存中的数据。
如:www.taobao.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。
www.360.com.cn的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
总结:根据以上两种模式,建议缓存策略为:判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。
3、清除缓存
webview.clearCache(boolean);
CacheManager.clear高版本中需要调用隐藏API。
4、控制大小
无系统API支持。
可选方式:定时统计缓存大小、按时间顺序删除缓存。
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private TextView mTitle;
private WebView mWebView;
private TextView mClear;
private String url;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
url = "https://wap.baidu.com/";
findView();
}
private void findView() {
mTitle = (TextView) findViewById(R.id.tv_topbar_title);
mWebView = (WebView) findViewById(R.id.mWebView);
mClear = (TextView) findViewById(R.id.clear);
mClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.clearCache(true); //清除缓存
}
});
initWebView();
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onLoadResource(WebView view, String url) {
Log.i(TAG, "onLoadResource url="+url); // 开始加载
super.onLoadResource(view, url);
}
@Override
public boolean shouldOverrideUrlLoading(WebView webview, String url) {
Log.i(TAG, "intercept url="+url);
// 重写此方法表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边
webview.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.e(TAG, "onPageStarted");
}
@Override
public void onPageFinished(WebView view, String url) {
String title = view.getTitle(); //得到网页标题
Log.e(TAG, "onPageFinished WebView title=" + title);
mTitle.setText(title);
mTitle.setVisibility(View.VISIBLE);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(getApplicationContext(), description, Toast.LENGTH_LONG).show();
}
});
mWebView.loadUrl(url);
}
private void initWebView() {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
// 设置缓存模式
if (NetUtils.isNetworkAvailable(MainActivity.this)) {
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
mWebView.getSettings().setCacheMode( WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
// webView.getSettings().setBlockNetworkImage(true);//把图片加载放在最后来加载渲染
// 支持多窗口
webView.getSettings().setSupportMultipleWindows(true);
// 开启 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//开启 database storage API 功能
mWebView.getSettings().setDatabaseEnabled(true);
// 开启 Application Caches 功能
// webView.getSettings().setAppCacheEnabled(true);
}
@Override
// 设置回退
// 覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack(); // goBack()表示返回WebView的上一页面
return true;
} else {
finish();
}
return super.onKeyDown(keyCode, event);
}
/***
* 防止WebView加载内存泄漏
*/
@Override
protected void onDestroy() {
super.onDestroy();
mWebView.removeAllViews();
mWebView.destroy();
}
}
AndroidManifest.xml 中加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Application Cache(简称 AppCache)似乎是为支持 Web App 离线使用而开发的缓存机制。它的缓存机制类似于浏览器的缓存(Cache-Control 和 Last-Modified)机制,都是以文件为单位进行缓存,且文件有一定更新机制。但 AppCache 是对浏览器缓存机制的补充,不是替代。
如果你还不了解什么叫做H5缓存,推荐这篇文章:H5 缓存机制浅析 - 移动端 Web 加载性能优化
1、缓存构成
根据setAppCachePath(String appCachePath)提供的路径,在H5使用缓存过程中生成的缓存文件。
2、缓存模式
无模式选择,通过setAppCacheEnabled(boolean flag)设置是否打开。默认关闭,即,H5的缓存无法使用。
3、清除缓存
找到调用setAppCachePath(String appCachePath)设置缓存的路径,把它下面的文件全部删除就OK了。
4、控制大小
通过setAppCacheMaxSize(long appCacheMaxSize)设置缓存最大容量,默认为Max Integer。
同时,可能通过覆盖WebChromeClient.onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)来设置缓存超过先前设置的最大容量时的策略。
...
String cacheDirPath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
Log.i(TAG, "cacheDirPath="+cacheDirPath);
/**设置Application Caches 缓存目录*/
mWebView.getSettings().setAppCachePath(cacheDirPath);
//开启 Application Caches 功能
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setAppCacheMaxSize(5*1024*1024); //5M
mWebView.getSettings().setAllowFileAccess(true); //使manifest生效
webview可以设置一个WebChromeClient对象,在其onReachedMaxAppCacheSize函数对扩充缓冲做出响应。代码如下:
mWebView.setWebChromeClient(new WebChromeClient(){
//扩充缓存的容量
@Override
public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(spaceNeeded * 2);
}
清除缓存:
/**
* 清除WebView缓存
*/
public void clearWebViewCache(){
//清理Webview缓存数据库
try {
deleteDatabase("webview.db");
deleteDatabase("webviewCache.db");
} catch (Exception e) {
e.printStackTrace();
}
//WebView 缓存文件
File appCacheDir = new File(getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath());
Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath());
//删除webview 缓存 缓存目录
if(appCacheDir.exists()){
deleteFile(appCacheDir);
}
}
权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
其次要修改http服务器中的配置,使其支持text/cache-manifest,我使用的是apach服务器,是windows版本的,在apache的conf文件夹中找到mime.types文件,打开后在文件的最后加上
“text/cache-manifest mf manifest”,
重启服务器即可。
WebView离线阅读就是使用WebView加载本地的html文档,上面说的H5缓存实质上已经实现了WebView离线阅读的功能,但是需要服务器的支持,本节我们自己来实现WebView的离线阅读而不依赖服务器。
首先来看看webview加载网络资源的情况:
url = "http://dfz.eastday.com/nanchang/u1ai17496_t11.html";
...
mOnline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl(url);
}
});
我们的思路是把html文档下载到本地,然后用webview加载本地文档即可。
private void DownloadArticle(String url){
String htmlCode = "";
try {
Document document = Jsoup.parse(new URL(url), 10000);
if (document != null){
String filePath = OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html"; //url进行MD5编码后作为文件名
htmlCode = document.html().toString();
HttpUtil.SaveTextToFile(filePath, htmlCode);
}
} catch (Exception e) {
e.printStackTrace();
}
}
mOffline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String filePath = "file://" + OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html";
mWebView.loadUrl(filePath);
}
});
可以看到,确实加载到了本地的html文档,但是图片没有了,版式也有些不对。其实看html源码
我们可以看到图片,版式等都是在加载html时实时从网络获取的,我们现在在离线状态下,当然获取不到这些内容了。所以我们还需要把需要的js、css、图片等文件也下载下来,在我们执行html文档的时候可以选择从本地加载这些资源。有了想法,实施。
/**
* 下载稿件html以及包含的图片,cs,js
* @param url
*/
private void DownloadArticle(String url){
String htmlCode = "";
try {
Document document = Jsoup.parse(new URL(url), 10000);
if (document != null){
String filePath = OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html";
htmlCode = document.html().toString();
htmlCode = replaceImage(document,"img","src",htmlCode); //替换成从本地获取image
htmlCode = replaceStyle(document,"link","href",htmlCode); //替换成从本地获取css
htmlCode = htmlCode.replace("Common.js", ""); //这篇稿件特殊情况,Common.js会弹出一个alert,屏蔽它
HttpUtil.SaveTextToFile(filePath, htmlCode);
}
} catch (Exception e) {
e.printStackTrace();
}
}
...
/**
* 替换中间的图片
* @param document
* @param tag
* @param attr
* @param htmlCode
*/
private String replaceImage(Document document, String tag, String attr, String htmlCode){
Elements imageDocuments = document.getElementsByTag(tag);
String imgURL = "";
Element element = null;
for(int i = 0; i < imageDocuments.size(); i++){
element = imageDocuments.get(i);
imgURL = element.attr(attr);
if (imgURL.isEmpty())
continue;
DownloadImage(getWholeURL(imgURL));
htmlCode = htmlCode.replace(imgURL, "file://" + OFFLINE_PATH_IMG + MD5Util.getMD5Str(getWholeURL(imgURL)));
}
return htmlCode;
}
...
/**
*替换中间的样式css
* @param document
* @param tag
* @param attr
* @param htmlCode
* @return
*/
private String replaceStyle(Document document, String tag, String attr, String htmlCode){
String initialUrl = "";
String replaceUrl = "";
String getBack = "";
String fileJS = "";
Element element = null;
Elements elements = document.getElementsByTag(tag);
for(int i = 0; i < elements.size(); i++){
element = elements.get(i);
initialUrl = element.attr(attr);
if (initialUrl.isEmpty())
continue;
replaceUrl = getWholeURL(initialUrl);
fileJS = OFFLINE_PATH_JS + MD5Util.getMD5Str(getWholeURL(replaceUrl));
if (!new File(fileJS).exists()){
getBack = HttpUtil.requestContentWithGet11(replaceUrl);
HttpUtil.SaveTextToFile(fileJS, getBack);
}
htmlCode = htmlCode.replace(initialUrl, "file://" + fileJS);
}
return htmlCode;
}
网页中需要通过JS代码来调用本地的Android代码,比如H5页面需要判断当前用户是否登录等。
利用JS代码调用JAVA代码,主要是用到WebView下面的一个函数:
public void addJavascriptInterface(Object obj, String interfaceName)
这个函数有两个参数:
它的意思就是向WebView注入一个obj对象,对象的别名为interfaceName,在JS中,我们就可以通过interfaceName这个别名来调用obj对象中的任何public方法。
我们实现这样一个效果,在上面的html中添加了一个按钮,当点击按钮时调用Android的Toast函数弹出一个toast消息。
先看android代码:
public class MyActivity extends Activity {
private WebView mWebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSBridge(), "android");
mWebView.loadUrl("file:///android_asset/web.html");
}
public class JSBridge {
//在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效
@JavascriptInterface
public void toastMessage(String message) {
Toast.makeText(getApplicationContext(), "JS--->Natvie:" + message, Toast.LENGTH_LONG).show();
}
}
下面我们看看html代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<h1>WebView加载本地HTMLh1>
<input type="button" value="js调native" onclick="ok()">
head>
<body>
<script type="text/javascript">
function ok() {
android.toastMessage("我是来自JS的消息!");
}
script>
body>
html>
前面给大家演示了如何通过JS调用Java代码,这里就反过来看看,如何在Native中调用JS的代码 。
本例的效果图如下:
在点击“求和”按钮时,调用webview中的JavaScript求和函数,将结果通过alert显示出来。
先看html代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<h1>WebView加载本地HTMLh1>
<input type="button" value="js调native" onclick="ok()">
head>
<body>
<script type="text/javascript">
function ok() {
android.toastMessage("我是来自JS的消息!");
}
function sum(i,m)
{
alert("Native--->JS sum=" + (i + m));
}
script>
body>
html>
在这里,我们写了一个求和函数sum(i,m) ,alert出求和结果
再来看看JAVA的调用代码:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mBtn = (Button) findViewById(R.id.btn);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.loadUrl("file:///android_asset/web.html");
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("javascript:sum(3,8)");
}
});
}
}
看看在JAVA中调用JS函数的方法:
String url = "javascript:methodName(params……);"
webView.loadUrl(url);
javascript:伪协议让我们可以通过一个链接来调用JavaScript函数 ,中间methodName是JavaScript中实现的函数 ,jsonParams是传入的参数列表 。
JAVA中如何得到JS中的返回值呢?
相信看完了上面Native和JS的互相调用,你一定知道了。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<h1>WebView加载本地HTMLh1>
<input type="button" value="js调native" onclick="ok()">
head>
<body>
<script type="text/javascript">
function ok() {
android.toastMessage("我是来自JS的消息!");
}
function sum(i,m)
{
var result = i+m;
alert("Native--->JS sum=" + result);
android.onSumResult(result);
}
script>
body>
html>
public class JSBridge {
//在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效
@JavascriptInterface
public void toastMessage(String message) {
Toast.makeText(getApplicationContext(), "JS--->Natvie:" + message, Toast.LENGTH_LONG).show();
}
@JavascriptInterface
public void onSumResult(int result) {
Toast.makeText(this,"received result:"+result,Toast.LENGTH_SHORT).show();
}
}
Android4.4之后,我们有新的方法在JAVA中获取JS的返回值:
首先给html的求和函数一个返回值:
function sum(i,m)
{
var result = i+m;
return result;
}
其次java代码时用evaluateJavascript方法调用:
mWebView.evaluateJavascript("sum(3,8)", new ValueCallback() {
@Override
public void onReceiveValue(String value) {
Toast.makeText(getApplicationContext(),"Android 4.4 received result:"+value,Toast.LENGTH_SHORT).show();
}
});
注意:
1. evaluateJavascript须在html加载完毕后执行,否则返回的value为null。
2. 上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。
3. evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。
Demo下载地址