想必大家在开发系统应用的过程中用到 WebView 的地方可能会遇到这样的加载崩溃问题
less
Caused by: java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes
at android.webkit.WebViewFactory.getProvider([WebViewFactory.java:235](http://webviewfactory.java:235/))
at android.webkit.WebView.getFactory([WebView.java:2681](http://webview.java:2681/))
at android.webkit.WebView.ensureProviderCreated([WebView.java:2676](http://webview.java:2676/))
at android.webkit.WebView.setOverScrollMode([WebView.java:2741](http://webview.java:2741/))
at android.view.View.<init>([View.java:4821](http://view.java:4821/))
at android.view.View.<init>([View.java:4962](http://view.java:4962/))
at android.view.ViewGroup.<init>([ViewGroup.java:660](http://viewgroup.java:660/))
at android.widget.AbsoluteLayout.<init>([AbsoluteLayout.java:55](http://absolutelayout.java:55/))
at android.webkit.WebView.<init>([WebView.java:659](http://webview.java:659/))
at android.webkit.WebView.<init>([WebView.java:604](http://webview.java:604/))
at android.webkit.WebView.<init>([WebView.java:587](http://webview.java:587/))
at android.webkit.WebView.<init>([WebView.java:574](http://webview.java:574/))
这是因为首次使用时,系统会进行检查,如果 UID 是 root 进程或者系统进程,直接抛出异常。sProviderInstance 是 WebViewFactoryProvider 的对象,主要提供创建 WebView 内核的机制。WebView在 Android 4.4 之前使用的是 Webkit 内核,在 Android 4.4 以后切换到了 Chromium 内核。Google 使用了工厂方法模式,优雅地切换 WebView 内核的实现方式。我们注意到只有 sProviderInstance 为空的时候系统才去检查进程,然后创建 sProviderInstance对象。所以这给了我们一个启发 ---- 能不能一开始就主动创建 sProviderInstance 对象,把她塞到 WebViewFactory 类里面,从而欺骗 API 绕过系统检查呢?
下面就要用到 Hook 的思想了,首先要找到一个合适的点,静态变量、单例是最佳选择,刚刚好 sProviderInstance 是静态的。那就开始拿它开刀,看看系统是怎么创建 sProviderInstance 的,我们自己也模仿它这么做。其实系统也是通过反射来做的
java
import android.annotation.SuppressLint;
import android.os.Build;
import com.atoto.utils.log.AtotoLogger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 描述:
* 创建者: IT 乐手
* 日期: 2025/4/25
*/
public class WebViewCompat {
private final static String TAG = "WebViewCompat";
@SuppressLint("SoonBlockedPrivateApi")
public static void hookWebView() {
int sdkInt = Build.VERSION.SDK_INT;
try {
Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
Field field = factoryClass.getDeclaredField("sProviderInstance");
field.setAccessible(true);
Object sProviderInstance = field.get(null);
if (sProviderInstance != null) {
AtotoLogger.i(TAG, "sProviderInstance isn't null");
return;
}
Method getProviderClassMethod;
if (sdkInt > 22) {
getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
} else if (sdkInt == 22) {
getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
} else {
AtotoLogger.i(TAG, "Don't need to Hook WebView");
return;
}
getProviderClassMethod.setAccessible(true);
Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
Constructor delegateConstructor = delegateClass.getDeclaredConstructor();
delegateConstructor.setAccessible(true);
Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
chromiumMethodName.setAccessible(true);
String chromiumMethodNameStr = (String) chromiumMethodName.get(null);
AtotoLogger.i(TAG, "chromiumMethodNameStr: " + chromiumMethodNameStr);
if (chromiumMethodNameStr == null) {
chromiumMethodNameStr = "create";
}
Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
if (staticFactory != null) {
sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
}
if (sProviderInstance != null) {
field.set("sProviderInstance", sProviderInstance);
AtotoLogger.i(TAG, "Hook success!");
} else {
AtotoLogger.i(TAG, "Hook failed!");
}
} catch (Throwable e) {
AtotoLogger.printException(TAG, e);
}
}
}
在实例化使用 WebView 前调用即可
java
hookWebView();
setContentView(R.layout.activity_main);
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.baidu.com");