学知识就是要掌握一些概念的内涵和外延。So,让我们看看与WebView相关的几个概念吧。
| 概念 | 内涵 | 外延 | 
|---|---|---|
| WebView | 一个绝对布局容器 | 用来展示或渲染Web页面 | 
| WebSettings | 一个抽象类,包含对WebView的设置方法。 | 用来对WebView进行设置,比如支持JS、缓存模式等 | 
| WebViewClient | 这个类的方法有一个特点,就是参数的第一项就是WebView,其他项是事件或数据信息。WebView通过该类对外通知页面加载相关的消息 | 用来在页面加载的各个阶段进行业务处理,处理加载错误情况,拦截页面内和页面外的请求 | 
| WebChromeClient | WebView通过该类,通知获取到站点图标、标题、加载进度,以及JS对话框等 | 用来更好地展示页面和交互 | 
理解了上面几个概念后,我们就可以处理一般的业务需求了。比如
设置缓存
缓存模式
获取到WebSettings然后调用其中的设置方法就可以了。
WebSettings webSettings = webView.getSettings();
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
缓存模式有LOAD_DEFAULT(默认)、LOAD_CACHE_ELSE_NETWORK(先缓存后网络)、LOAD_NO_CACHE(不要缓存)、LOAD_CACHE_ONLY(只要缓存),可以根据业务要求选择。
缓存机制
(1)浏览器缓存机制
- 概念:通过 HTTP 协议头里的 Cache-Control(或 Expires)和 Last-Modified(或 Etag)等字段来控制文件缓存的机制。
- 适用:适用于 Web 的静态资源文件。
(2)Dom Storage(Web Storage)
- 概念:通过存储字符串的 Key/Value 对来提供,并提供 5MB (不同浏览器可能不同,分 HOST)的存储空间(Cookies 才 4KB)。分为 sessionStorage 和 localStorage。
- 适用:代替掉将一些不需要让服务器知道的信息存储到 cookies 里的这种传统方法。webSettings.setDomStorageEnabled(true);
(3)Web SQL Database
- 概念:基于 SQL 的数据库存储机制,用于存储适合数据库的结构化数据。
- 适用:Web SQL Database 存储机制不再推荐使用,将来也不再维护,而是推荐使用 AppCache 和 IndexedDB。
webSettings.setDatabaseEnabled(true);
final String dbPath = getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
webSettings.setDatabasePath(dbPath);
(4)Application Cache(AppCache)
- 概念:为支持 Web App 离线使用而开发的缓存机制。以文件为单位进行缓存,且文件有一定更新机制。
- 适用:AppCache 是对浏览器缓存机制的补充,不是替代。不推荐使用了,标准也不会再支持。
webSettings.setAppCacheEnabled(true);
final String cachePath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
webSettings.setAppCachePath(cachePath);
webSettings.setAppCacheMaxSize(5*1024*1024);
(5)Indexed Database
- 概念:NoSQL 数据库,类似于 Dom Storage 的 key-value 的存储方式,但功能更强大,且存储空间更大。
- 适用:用于存储大块或复杂结构的数据,提供更大的存储空间,使用起来也比较简单。可以作为 Web SQL Database 的替代。不太适合静态文件的缓存。webSettings.setJavaScriptEnabled(true);
(6)File System API
- 概念:为 Web App 提供了一个虚拟的文件系统,运行在沙盒中
- 适用:任何需要通过文件来管理数据,或通过文件系统进行数据管理的场景都比较适合。到目前,Android 系统的 Webview 还不支持 File System API。
处理页面导航
方法一
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}
方法二
@Override
public void onBackPressed() {
    if (contentWeb.canGoBack()) {
        contentWeb.goBack();
    } else {
        super.onBackPressed();
    }
}
与Activity/Fragment生命周期相适应
参考WebViewFragment实现
/**
 * A fragment that displays a WebView.
 * <p>
 * The WebView is automically paused or resumed when the Fragment is paused or resumed.
 */
public class WebViewFragment extends Fragment {
    private WebView mWebView;
    private boolean mIsWebViewAvailable;
    public WebViewFragment() {
    }
    /**
     * Called to instantiate the view. Creates and returns the WebView.
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (mWebView != null) {
            mWebView.destroy();
        }
        mWebView = new WebView(getActivity());
        mIsWebViewAvailable = true;
        return mWebView;
    }
    /**
     * Called when the fragment is visible to the user and actively running. Resumes the WebView.
     */
    @Override
    public void onPause() {
        super.onPause();
        mWebView.onPause();
    }
    /**
     * Called when the fragment is no longer resumed. Pauses the WebView.
     */
    @Override
    public void onResume() {
        mWebView.onResume();
        super.onResume();
    }
    /**
     * Called when the WebView has been detached from the fragment.
     * The WebView is no longer available after this time.
     */
    @Override
    public void onDestroyView() {
        mIsWebViewAvailable = false;
        super.onDestroyView();
    }
    /**
     * Called when the fragment is no longer in use. Destroys the internal state of the WebView.
     */
    @Override
    public void onDestroy() {
        if (mWebView != null) {
            mWebView.destroy();
            mWebView = null;
        }
        super.onDestroy();
    }
    /**
     * Gets the WebView.
     */
    public WebView getWebView() {
        return mIsWebViewAvailable ? mWebView : null;
    }
}
展示页面加载进度
旧有的实现
参考Android: The progress bar in the window's title does not display文中的一种实现,然而看了下发布时间是2010.06。注意:那时候用的是Android 2.3,BrowserActivity不是继承自AppCompactActivity,使用的主题也不是AppCompact主题。
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    requestWindowFeature(Window.FEATURE_PROGRESS);
    currentURL = BrowserActivity.this.getIntent().getExtras().getString("currentURL");
    setContentView(R.layout.browser);
    setProgressBarIndeterminateVisibility(true);
    setProgressBarVisibility(true);
    try {
        mWebView = (WebView) findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new browserActivityClient());
        mWebView.setWebChromeClient(new WebChromeClient() {
           public void onProgressChanged(WebView view, int progress) {
               setProgress(progress * 100);
              if(progress == 100) {
                 setProgressBarIndeterminateVisibility(false);
                 setProgressBarVisibility(false);
              }
           }
        });
        mWebView.loadUrl(currentURL);
    } catch (Exception e) {
        Log.e(getClass().getSimpleName(), "Browser: " + e.getMessage());
        Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
    } 
}
自定义实现
- 思路:在webView内部或外部添加一个ProgressBar(水平样式),并通过WebChromeClient获知页面的加载进度,进而调整ProgressBar的进度。
- 
实现: WebChromeClient webChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { progressBar.setVisibility(View.GONE); } else { progressBar.setVisibility(View.VISIBLE); progressBar.setProgress(newProgress); } } }; webView.setWebViewClient(webViewClient);