本篇博客要实现的是一个离线下载和离线阅读的功能,这是很多阅读类app都常见的一个功能,典型的应用就是网易新闻。什么是离线下载?其实这个概念是比较模糊,是离线之后下载呢,还是下载之后离线,但稍微有点脑子的人都知道没有网络之后怎么下载呢?所以离线下载这个功能是”在有网络的情况下,把资源下载到本地“,离线阅读就是”在没有网络或者网络不好的时候,阅读本地好缓存的文章资源“。这样就很清楚我们要的这两个具体的功能需求了。
实现思路
小巫这里提供两个实现思路,一个就是自己写逻辑,一个是通过WebView本身自带的缓存功能来实现。
先来说第一个思路:
定义一个离线下载的服务Service
启动后台服务Service来执行异步下载
存储到本地数据库中
每一次加载url之前,先判断数据库是否存在缓存内容
5.如果存在缓存,优先加载本地缓存,如果不存在,才执行联网请求
第二个思路就是本篇 博客要讲的通过WebView自带的缓存功能来实现离线阅读
这里小巫接着上一篇博客的例子来添加代码。
参考1:http://www.open-/lib/view/open1392188052301.html
参考2:http://87426628./blog/static/606936189183417725/
Android WebView缓存可以分为页面缓存和数据缓存
页面缓存是指加载一个网页时html、JS、CSS等页面或者资源数据。
数据缓存分别为两种:AppCache和DOM Storage(Web Storage)。
AppCache也是我们的H5缓存,我们可以设置缓存的目录
Dom Storage具有Session Storage和Local Storage两种,前者是会话级别的存储,页面关闭之后就消失了,后者是本地化存储。
如果我们手机有root的权限的话,就可以看到/data/data/package_name/下的文件目录,我们就会发现webview为我们创建
app_webview,这个应该就是webview的缓存目录的位置。
public void initWebView() {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);
// 建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK
mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 设置缓存模式
// 开启DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
// 开启database storage API功能
mWebView.getSettings().setDatabaseEnabled(true);
String cacheDirPath = getFilesDir().getAbsolutePath()
APP_CACHE_DIRNAME;
Log.i(“cachePath”, cacheDirPath);
// 设置数据库缓存路径
mWebView.getSettings().setDatabasePath(cacheDirPath); // API 19 deprecated
// 设置Application caches缓存目录
mWebView.getSettings().setAppCachePath(cacheDirPath);
// 开启Application Cache功能
mWebView.getSettings().setAppCacheEnabled(true);
Log.i(“databasepath”, mWebView.getSettings().getDatabasePath());
}
这里加载我博客里的一篇文章:
public void findView() {
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 view, String url) {
Log.i(TAG, “intercept url=” + url);
view.loadUrl(url);
return true;
}
// 页面开始时调用
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.e(TAG, “onPageStarted”);
super.onPageStarted(view, url, favicon);
}
// 页面加载完成调用
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toast.makeText(getApplicationContext(), “”, Toast.LENGTH_LONG)
.show();
}
});
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
Log.e(TAG, "onJsAlert " + message);
Toast.makeText(getApplicationContext(), message,
Toast.LENGTH_SHORT).show();
result.confirm();
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url,
String message, JsResult result) {
Log.e(TAG, "onJsConfirm " + message);
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
Log.e(TAG, "onJsPrompt " + url);
return super.onJsPrompt(view, url, message, defaultValue,
result);
}
});
mWebView.loadUrl(url);
}
完整代码
package com.infzm.webview;
import java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import .Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebSettings.RenderPriority;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
private WebView mWebView;
private Button nightModeBtn;
private Button lightModeBtn;
// -----
private static final String TAG = MainActivity.class.getSimpleName();
private static final String APP_CACHE_DIRNAME = “/webcache”; // web缓存目录
private String url; // 网页url
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) this.findViewById(R.id.webview);
nightModeBtn = (Button) this.findViewById(R.id.btn_nightmode);
lightModeBtn = (Button) this.findViewById(R.id.btn_lightmode);
nightModeBtn.setOnClickListener(this);
lightModeBtn.setOnClickListener(this);
// webView.loadUrl(“”);
WebSettings settings = mWebView.getSettings();
// 设置javaScript可用
settings.setJavaScriptEnabled(true);
// 绑定javaScript接口,可以实现在javaScript中调用我们的Android代码
// webView.addJavascriptInterface(new WebAppInterface(this), “Android”);
// webView.setWebViewClient(new MyWebViewClient());
// 加载assets目录下的html页面
// mWebView.loadUrl(“file:///android_asset/01.html”);
url = “/wwj_748/article/details/44810283”;
findView();
}
public void findView() {
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 view, String url) {
Log.i(TAG, “intercept url=” + url);
view.loadUrl(url);
return true;
}
// 页面开始时调用
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.e(TAG, “onPageStarted”);
super.onPageStarted(view, url, favicon);
}
// 页面加载完成调用
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toast.makeText(getApplicationContext(), “”, Toast.LENGTH_LONG)
.show();
建议
当我们出去找工作,或者准备找工作的时候,我们一定要想,我面试的目标是什么,我自己的技术栈有哪些,近期能掌握的有哪些,我的哪些短板 ,列出来,有计划的去完成,别看前两天掘金一些大佬在驳来驳去 ,他们的观点是他们的,不要因为他们的观点,膨胀了自己,影响自己的学习节奏。基础很大程度决定你自己技术层次的厚度,你再熟练框架也好,也会比你便宜的,性价比高的替代,很现实的问题但也要有危机意识,当我们年级大了,有哪些亮点,与比我们经历更旺盛的年轻小工程师,竞争。
无论你现在水平怎么样一定要持续学习没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!!!!!!!
准备想说怎么样写简历,想象算了,我觉得,技术就是你最好的简历
我希望每一个努力生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
资料领取
点击这里免费获取
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术
因为我们很辛苦,我们应得的。
有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
[外链图片转存中…(img-8Jn7YDBg-1644035612403)]
[外链图片转存中…(img-KxjKCeNw-1644035612404)]
资料领取
点击这里免费获取
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术