美文网首页DevSupport
性能优化之Web View预加载

性能优化之Web View预加载

作者: 有没有口罩给我一个 | 来源:发表于2019-10-03 15:14 被阅读0次

    用过WebView的都知道,在首次WebView时启动一些系统服务,而这一过程是非常的耗时,经过测试根据不同机型耗时也不一样,性能好的机型上可能200毫秒以上,而性能稍微差点的500毫秒以上,这是不可接受的,我最近也在看这块的东西,发现很多文章基本都是在讲使用复用池对WebView做优化,我个人觉得这也是一个优化点,但是这并不能从根本上解决问题,复用池也就是仅仅是对WebView的渲染着了优化,所以并不能从根本解决这个性能问题,而且这种方式不好控制,容易造成内存泄漏,为了解决问题而引入其他问题,这是得不偿失的,最近几天经过大量阅读网上的文章和阅读WbeView的源码,发现WebView首次加载慢的原因就是,在启动过过程中,要启动chrome相关的服务,而这些过程是相当耗时的,所以我们的优化点就是,是否可以在应用启动时,提前启动chrome的服务?答案是可以的,在这之后我看了滴滴的Booster的源码,他们优化方案也是通过提前启动chrome的服务,下面来看看是怎么实现的?

    怎么绕过系统服务Hide API?

    相比大家应该很想清楚Android P已经限制了很多非公开接口,就是通过反射或者JNI访问非公开接口时会触发警告/异常等:然后借助系统类去调用反射
    首先,我们通过反射 API 拿到 getDeclaredMethod A方法,不存在问题;
    然后,我们通过刚刚反射拿到的A方法去反射调用 getDeclardMethod。这里我们就实现了以系统身份去反射的目的——反射相关的 API 都是系统类,因此我们反射的A方法也是被系统类加载的方法;所以我们的反射方法调用的 getDeclardMethod 会被认为是系统调用的,可以反射任意的方法,这里就提这些,进入正题。

    首先来看看原来怎么实现:

    object WebViewHelper {
    private const val WEB_VIEW_FACTORY_CLASS_NAME = "android.webkit.WebViewFactory"
    private const val FACTORY_METHOD_NAME_GET_PROVIDER = "getProvider"
    
    fun preloadWebView(application: Application) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //将在系统闲置的时候执行
            application.mainLooper.queue.addIdleHandler {
                startChromiumEngine()
                false//返回false将会移除这个IdleHandler
            }
        }
    }
    
    @SuppressLint("PrivateApi")
    private fun startChromiumEngine() {
        try {
            MeasureLaunchTime.startRecord()
            val webViewFactoryClass = Class.forName(WEB_VIEW_FACTORY_CLASS_NAME)
            ReflectHelper.invokeStaticMethod<Any>(webViewFactoryClass, FACTORY_METHOD_NAME_GET_PROVIDER)
                ?.let {
                    ReflectHelper.invokeMethod<Any>(
                        it,
                        "startYourEngines",
                        arrayOf(Boolean::class.java),
                        arrayOf(true)
                    )
                }
            MeasureLaunchTime.endRecord("Start chromium engine complete: ")
        } catch (t: Throwable) {
            Log.e("tag", "Start chromium engine error", t)
        }
    }
    

    }

    注释写得很清楚就不多介绍,唯一要说的是IdleHandler这个类,addIdleHandler是等到系统闲置了才执行,而返回值为false,就是执行完成会讲这个addIdleHandler移除,当然你可以手动移除,如果你不信请看下面Handler的源码:

     Message next() {
    
     ............略..........
    
        for (;;) {
            synchronized (this) {
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
    
            // 循环遍历所有的 IdleHandler 回调
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null;
                // 执行IdleHandler 的 queueIdle 方法,如果queueIdle 返回false,就会移除这个IdleHandler
                boolean keep = false;
                ............略.............
    
                keep = idler.queueIdle();
                 ...........略.............
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
    

    最后在贴反射帮助类:

    object ReflectHelper {
    
    @JvmStatic
    fun <T> invokeStaticMethod(
        kClass: Class<*>,
        name: String
    ): T? = invokeStaticMethod<T>(kClass, name, arrayOf(), arrayOf())
    
    @JvmStatic
    fun <T> invokeStaticMethod(
        kClass: Class<*>,
        name: String,//方法名
        paramTypes: Array<Class<*>>,//参数类型
        paramValue: Array<Any>//参数值
    ): T? {
        if (paramTypes.size != paramValue.size) return null
        return getMethod(kClass, name, paramTypes)?.run {
            isAccessible = true
            invoke(kClass, *paramValue) as? T
        }
    }
    
    
    /**
     * 调用普通方法
     */
    @JvmStatic
    fun <T> invokeMethod(
        obj: Any,
        name: String,//方法名
        paramTypes: Array<Class<*>>,//参数类型
        paramValue: Array<Any>//参数值
    ): T? {
        if (paramTypes.size != paramValue.size) return null
        return getMethod(obj.javaClass, name, paramTypes)?.run {
            isAccessible = true
            invoke(obj, *paramValue) as? T
        }
    }
    
    
    @JvmStatic
    private fun getMethod(
        kClass: Class<*>,
        name: String,//方法名
        paramTypes: Array<Class<*>>//参数类型
    ): Method? {
        return try {
            //注意getDeclaredMethod和getMethod方法的区别
            kClass.getDeclaredMethod(name, *paramTypes)
        } catch (e: NoSuchMethodException) {
            //没有父类,说明这个类没有定义这个方法
            val parent = kClass.superclass ?: return null
            //如果还有父类,就从父类去找这个方法
            getMethod(parent, name, paramTypes)
        }
    }
    }
    

    最后看看启动耗时的时间是:

    Start chromium engine complete: 262 ms
    

    文章到此WebView结束了。

    相关文章

      网友评论

        本文标题:性能优化之Web View预加载

        本文链接:https://www.haomeiwen.com/subject/zmpdpctx.html