用过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结束了。
网友评论