WebView组件封装(一)——怎样使用全局缓存池管理提高WebView加载速度

4,084 阅读5分钟

前言

当用户浏览页面时,快速的页面加载速度对用户体验至关重要。而在应用中使用WebView展示页面时,缓慢的加载速度也会直接影响到用户的体验。针对WebView渲染速度和加载速度慢的问题,全局缓存池管理技术应运而生。

本文将介绍全局缓存池技术在使用WebView加载网页中的应用,通过本文,你将了解到如何使用全局缓存池管理技术来大幅提升WebView的加载速度,避免用户长时间的等待,从而为用户带来更好的使用体验。

效果图

直接上图,方便大家直观上看出优化后的效果

全局WebVIew优化前的加载速度.gif

全局WebView优化后的加载速度.gif

全局WebView的好处

  1. 简化开发:使用全局WebView无需在多个Activity或Fragment中重复定义WebView,大大减少代码量,提高开发效率
  2. 统一性:全局WebView统一了应用内的WebView样式和行为,保证了用户体验的一致性,增加用户粘性
  3. 优化性能:多个WebView的情况下,每个WebView都需要占用一定的内存和CPU,而使用全局WebView可以减少这种资源浪费,从而优化应用的性能
  4. 方便维护:在多个WebView的情况下,每个WebView的维护和更新都需要额外的工作,而使用全局WebView只需要在一个地方进行维护和更新即可,方便快捷

创建全局缓存池管理WebView

一、在Application的时候,创建一个WebView的缓存池,缓存池存放3个WebView的实例
     private lateinit var mWebViewPool: Array<PkWebView?>
     val WEB_VIEW_COUNT = 3
    /**
     * 初始化WebView池
     */
    fun initWebViewPool(context: Context?, userAgent: String = "") {
        mWebViewPool = arrayOfNulls(WEB_VIEW_COUNT)
        if (context == null) return
        for (i in 0 until WEB_VIEW_COUNT) {
            mWebViewPool[i] = createWebView(context, userAgent)
        }
    }
    private fun createWebView(context: Context, userAgent: String): PkWebView? {
        val webView = PkWebView(context)
        //初始化WebView的参数等
        WebViewManager.instance.apply {
            initWebViewSetting(webView, userAgent)
            initWebChromeClient(webView)
            initWebClient(webView)
        }
        return webView
    }
  • 上面一共创建了3个WebView,createWebView 函数用于创建一个新的 WebView 对象,并初始化 WebView 的相关参数和配置
  • 这里将userAgent提供出去,因为不同的项目可能UserAgent值不一样,这里需要注意webSettings.getUserAgentString()会获取到手机型号、浏览器版本和操作系统版本等信息
二、获取WebView对象
    /**
     * 获取webView
     */
    fun getWebView(): PkWebView? {
        checkIsInitialized()
        for (i in 0 until WEB_VIEW_COUNT) {
            if (mWebViewPool[i] != null) {
                val webView = mWebViewPool[i]
                mWebViewPool[i] = null
                return webView
            }
        }
        return null
    }

    private fun checkIsInitialized() {
        if (!::mWebViewPool.isInitialized) {
            throw UninitializedPropertyAccessException("Please call the PkWebViewInit.init method for initialization in the Application")
        }
    }

这里大家可能会有疑问,为什么是3个Fragment? 假设ViewPager中有三个WebView 的Fragment,没有做懒加载的时候,首页第一次直接切到第二个Fragment,那这三个Fragment都会被初始化,也就是可能会消耗3个WebView

这里将mWebViewPool[i]=null,起到的是标识作用

三、销毁webView对象
    /**
     * Activity销毁时需要释放当前WebView
     */
    fun releaseWebView(webView: PkWebView?) {
        checkIsInitialized()
        webView?.apply {
            stopLoading()
            removeAllViews()
            clearHistory()
            destroy()
            (parent as ViewGroup?)?.removeView(this)
            for (i in 0 until WEB_VIEW_COUNT) {
                if (mWebViewPool[i] == null) {
                    mWebViewPool[i] = webView
                    return
                }
            }
        }

    }

使用很简单我就不细说了,releaseWebView是在Fragment/Activity的onDestroy中进行调用,这里需要注意,还需要将获得的WebView在onDestroy中设置为null

    override fun onDestroy() {
        WebViewPool.instance.releaseWebView(mWebView)
        mWebView = null
        super.onDestroy()
    }

当我们第一次运行的时候,一切都是ok的,也明显速度变快了。但是当我们点击退出,再次进入的时候,显示一片空白,这是为什么呢? 这是因为我们在onDestroy的时候调用了webView的destroy()方法

那么webView的destory()方法做了哪些事情呢? Android平台版本的不同,WebView销毁的效果可能也有所不同。通常情况下,WebView在销毁时会做以下三件事情

  1. WebViewClient和WebViewChromeClient的引用解除:WebView中的WebViewClient和WebViewChromeClient这两个类可能会引用当前Activity或Fragment,如果在WebView销毁时没有将这两个类引用解除,就有可能出现内存泄漏的问题

  2. 所有异步任务的取消:WebView中有很多异步任务,例如图片加载、javaScript执行等等。在销毁WebView时,需要将这些任务取消,防止出现线程泄漏的问题

  3. WebView相关组件和线程的销毁:WebView内部包含了很多组件和线程,例如:WebViewCore、JavaScriptCore、WebViewWorker等等

WebView的destory()不一定能够完全销毁WebView,因为WebView内部可能存在一些难以管理和追踪的资源和线程,例如JavaScript执行线程、标准库线程等等,所以在使用WebView的时候,我们需要及时释放WebView对象

如何解决webView.destroy()之后再次启动的时候显示空白问题呢?

  • 1.不调用WebView的destory()?也可以正常运行,但是资源没法释放
  • 2、既然还要destroy,那destory之后,再重新设置WebViewClient、WebChromeClient这些参数呢?其实也不可行哈。因为此时调用完之后webView对象已无法再次使用,WebView所占用的资源都被释放了,并且WebView对象不再关联任何的Activity或Fragment
  • 3、重新创建WebView,并设置WebViewClient、WebChromeClient这些参数,这样是可以的。
    fun releaseWebView(webView: PkWebView?) {
        checkIsInitialized()
        webView?.apply {
            stopLoading()
            removeAllViews()
            clearHistory()
            destroy()
            (parent as ViewGroup?)?.removeView(this)
            for (i in 0 until WEB_VIEW_COUNT) {
                if (mWebViewPool[i] == null) {
                    mWebViewPool[i] = createWebView(webView.context,mUserAgent)
                    return
                }
            }
        }
    }

总结

该文章介绍了全局缓存池管理技术在使用 WebView 加载网页中的应用。通过该技术的实现,我们可以极大地提升 WebView 加载网页的速度,让用户获得更好的使用体验。如果你对该技术或者本文有任何问题和意见,可以前往本项目的 Github 地址 github.com/Peakmain/Pk… 进行讨论和交流。

全局WebView缓存池的源码位置:WebViewPool.kt