}
public AwServiceWorkerController getDefaultServiceWorkerController() {
synchronized (mLock) {
if (mDefaultServiceWorkerController == null) {
ensureChromiumStartedLocked(true);
}
}
return mDefaultServiceWorkerController;
}
public android.webkit.WebIconDatabase getWebIconDatabase() {
synchronized (mLock) {
ensureChromiumStartedLocked(true);
if (mWebIconDatabase == null) {
mWebIconDatabase = new WebIconDatabaseAdapter();
}
}
return mWebIconDatabase;
}
public WebStorage getDefaultWebStorage() {
synchronized (mLock) {
if (mDefaultWebStorage == null) {
ensureChromiumStartedLocked(true);
}
}
return mDefaultWebStorage;
}
public WebViewDatabase getDefaultWebViewDatabase(final Context context) {
synchronized (mLock) {
ensureChromiumStartedLocked(true);
if (mDefaultWebViewDatabase == null) {
mDefaultWebViewDatabase = new WebViewDatabaseAdapter(mFactory,
HttpAuthDatabase.newInstance(context, HTTP_AUTH_DATABASE_FILE),
mDefaultBrowserContext);
}
}
return mDefaultWebViewDatabase;
}
}
WebViewChromiumFactoryProvider.java
经过上面对的简单分析,我们大概知道了WebViewChromiumAwInit.startChromiumLocked
是被 ensureChromiumStartedLocked
方法调用的,而ensureChromiumStartedLocked
方法会被以下几个方法调用,那我们接下来的工作就需要找到下面这几个方法到底被谁调用了。
-getAwTracingController
-getAwProxyController
-startYourEngines
-getStatics
-getDefaultGeolocationPermissions
-getDefaultServiceWorkerController
-getWebIconDatabase
-getDefaultWebStorage
-getDefaultWebViewDatabase
到这里,分享我的一个土方法,我们要找到底哪个地方会调用这些方法,那就找一个不认识的、看上去不会被别人提起的方法,进行 google,我们一眼就选中了getDefaultServiceWorkerController
方法,没办法,谁叫我不认识你呢。虽然方法笨,但是架不住效率啊。于是乎,我们把它揪出来了 - WebViewChromiumFactoryProvider.java
我们大概了解一下 WebViewChromiumFactoryProvider
大概是个什么角色,WebViewChromiumFactoryProvider
实现了 WebViewFactoryProvider
接口,简单理解就是 WebView
的工厂,App 如果要创建 WebView
,就会通过 WebViewFactoryProvider
接口的实现类进行 createWebView
,所以其实就是个工厂模式。通过抽象规范 api,保证兼容性和可移植性可扩展性。
我们在这个文件中也如愿以偿的看到了上面列出来的几个方法的调用。WebViewChromiumFactoryProvider
在接口方法的实现中,调用了WebViewChromiumAwInit
里的一系列方法,如下:
//WebViewChromiumFactoryProvider.java
@Override
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
}
//我们截取一段
@Override
public GeolocationPermissions getGeolocationPermissions() {
return mAwInit.getDefaultGeolocationPermissions();
}
@Override
public CookieManager getCookieManager() {
return mAwInit.getDefaultCookieManager();
}
@Override
public ServiceWorkerController getServiceWorkerController() {
synchronized (mAwInit.getLock()) {
if (mServiceWorkerController == null) {
mServiceWorkerController = new ServiceWorkerControllerAdapter(
mAwInit.getDefaultServiceWorkerController());
}
}
return mServiceWorkerController;
}
@Override
public TokenBindingService getTokenBindingService() {
return null;
}
@Override
public android.webkit.WebIconDatabase getWebIconDatabase() {
return mAwInit.getWebIconDatabase();
}
@Override
public WebStorage getWebStorage() {
return mAwInit.getDefaultWebStorage();
}
@Override
public WebViewDatabase getWebViewDatabase(final Context context) {
return mAwInit.getDefaultWebViewDatabase(context);
}
WebViewDelegate getWebViewDelegate() {
return mWebViewDelegate;
}
WebViewContentsClientAdapter createWebViewContentsClientAdapter(WebView webView,
Context context) {
try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped(
“WebViewChromiumFactoryProvider.insideCreateWebViewContentsClientAdapter”)) {
return new WebViewContentsClientAdapter(webView, context, mWebViewDelegate);
}
}
void startYourEngines(boolean onMainThread) {
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped(
“WebViewChromiumFactoryProvider.startYourEngines”)) {
mAwInit.startYourEngines(onMainThread);
}
}
boolean hasStarted() {
return mAwInit.hasStarted();
}
3. 确定问题
我们上面通过阅读 WebViewChromiumFactoryProvider.java
、WebViewChromiumAwInit.java
这两个文件具体代码实现,有了一个比较清晰的思路。
App 在初始化过程中,调用到了 WebViewFactoryProvider
接口实现类的某个方法,这个方法调用了 WebViewChromiumAwInit
的下面方法中的其中一个或者多个。那其实问题就清晰了,我们只需要找到,我们 app 启动阶段到底哪行代码,会调用到 WebViewFactoryProvider
接口某个接口方法就行。
-getAwTracingController
-getAwProxyController
-startYourEngines
-getStatics
-getDefaultGeolocationPermissions
-getDefaultServiceWorkerController
-getWebIconDatabase
-getDefaultWebStorage
-getDefaultWebViewDatabase
由于 WebView
的代码并不会打包进 app 里,App 用的 WebView
内核都是用的 Android 系统负责内置、升级的 WebView
内核代码,所以通过 transform 的方式进行 hook 调用是不行的,这里我们采用动态代理的方式,对 WebViewFactoryProvider
接口方法进行 hook,我们通过动态代理生成一个 proxy 对象,通过反射的方式,替换掉 android.webkit.WebViewFactory
的 sProviderInstance
对象。
##WebViewFactory
@SystemApi
public final class WebViewFactory{
//…
@UnsupportedAppUsage
private static WebViewFactoryProvider sProviderInstance;
//…
}
##动态代理
try {
Class clas = Class.forName(“android.webkit.WebViewFactory”);
Method method = clas.getDeclaredMethod(“getProvider”);
method.setAccessible(true);
Object obj = method.invoke(null);
Object hookService = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getSuperclass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d(“zttt”, "hookService method: " + method.getName());
new RuntimeException(method.getName()).printStackTrace();
return method.invoke(obj, args);
}
});
Field field = clas.getDeclaredField(“sProviderInstance”);
field.setAccessible(true);
field.set(null, hookService);
} catch (Exception e) {
e.printStackTrace();
}
替换掉 sProviderInstance
之后,我们就可以在我们的代理逻辑中,加上断点来进行调试,最终找到了造成 WebView非预期初始化的始作俑者:WebSettings.getDefaultUserAgent
。
4. 解决问题
事情到这里就好解决了,只需要对 WebSettings.getDefaultUserAgent
进行编译期的Hook,重定向到带缓存defaultUserAgent
的相关方法就行了,本地有缓存则直接读取,本地没有则立即读取,得益于之前我在项目中实现的使用方便的 配置化 Hook 框架,这种小打小闹的 Hook 工作不到一分钟就能完成。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-WzsqbWjl-1712644277103)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!