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");
相关推荐
vocal36 分钟前
我的安卓第一课:四大组件之一Service
android
用户2018792831675 小时前
如何利用AI工具快速学习Android源码
android
音视频牛哥6 小时前
Android 平台RTSP/RTMP播放器SDK接入说明
android·音视频·大牛直播sdk·rtsp播放器·rtmp播放器·rtmp低延迟播放·rtmpplayer
aningxiaoxixi7 小时前
Android Framework 之 AudioDeviceBroker
android·windows·ffmpeg
~Yogi7 小时前
今日学习:工程问题(场景题)
android·学习
奔跑吧 android7 小时前
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
android·bluetooth·bt·aosp13
移动开发者1号7 小时前
Android Activity状态保存方法
android·kotlin
移动开发者1号7 小时前
Volley源码深度分析与设计亮点
android·kotlin
张风捷特烈7 小时前
每日一题 Flutter#7,8 | 关于 State 两道简答题
android·flutter·面试
计蒙不吃鱼16 小时前
一篇文章实现Android图片拼接并保存至相册
android·java·前端