android 解决系统级应用 WebView 加载崩溃的问题

想必大家在开发系统应用的过程中用到 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");
相关推荐
CYRUS_STUDIO32 分钟前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师1 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师1 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫1 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白1 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度
dpxiaolong3 小时前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
tangweiguo030519874 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android
老狼孩111224 小时前
2025新版懒人精灵零基础及各板块核心系统视频教程-全分辨率免ROOT自动化开发
android·机器人·自动化·lua·脚本开发·懒人精灵·免root开发
打死不学Java代码4 小时前
PaginationInnerInterceptor使用(Mybatis-plus分页)
android·java·mybatis