Android 系统应用使用webview 报错

我们在使用webview 的时候,当在主配置文件中配置了 android:sharedUserId="android.uid.system" 这个属性时,报以下错误

E/AndroidRuntime(24145): Caused by: java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes
E/AndroidRuntime(24145):        at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:100)
E/AndroidRuntime(24145):        at android.webkit.WebView.getFactory(WebView.java:2194)
E/AndroidRuntime(24145):        at android.webkit.WebView.ensureProviderCreated(WebView.java:2189)
E/AndroidRuntime(24145):        at android.webkit.WebView.setOverScrollMode(WebView.java:2248)
E/AndroidRuntime(24145):        at android.view.View.(View.java:3617)
E/AndroidRuntime(24145):        at android.view.View.(View.java:3711)
E/AndroidRuntime(24145):        at android.view.ViewGroup.(ViewGroup.java:497)
E/AndroidRuntime(24145):        at android.widget.AbsoluteLayout.(AbsoluteLayout.java:55)
E/AndroidRuntime(24145):        at android.webkit.WebView.(WebView.java:544)
E/AndroidRuntime(24145):        at android.webkit.WebView.(WebView.java:489)
E/AndroidRuntime(24145):        at android.webkit.WebView.(WebView.java:472)
E/AndroidRuntime(24145):        at android.webkit.WebView.(WebView.java:459)
E/AndroidRuntime(24145):        at android.webkit.WebView.(WebView.java:449)
E/AndroidRuntime(24145):        at com.hzkj.system.usewebview.MainActivity.onCreate(MainActivity.java:18)
E/AndroidRuntime(24145):        at android.app.Activity.performCreate(Activity.java:6222)
E/AndroidRuntime(24145):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
E/AndroidRuntime(24145):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
E/AndroidRuntime(24145):        ... 10 more
W/ActivityManagerService( 1013):   Force finishing activity 1 com.hzkj.system.usewebview/.MainActivity

为何会抛出异常? 异常信息可以看出 是在 WebViewFactory.java 的getProvider 方法 抛出的。源码路径为
frameworks/base/core/java/android/webkit/WebViewFactory.java

static WebViewFactoryProvider getProvider() {
        synchronized (sProviderLock) {
            // For now the main purpose of this function (and the factory abstraction) is to keep
            // us honest and minimize usage of WebView internals when binding the proxy.
            if (sProviderInstance != null) return sProviderInstance; //如果sProviderInstance不为空直接返回
            
          //1,判断,如果是系统id ,则抛出异常。
            final int uid = android.os.Process.myUid();
            if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) {
                throw new UnsupportedOperationException(
                        "For security reasons, WebView is not allowed in privileged processes");
            }

            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
            try {
                //2
                Class providerClass = getProviderClass();

                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
                try {
                    //3 给 sProviderInstance 赋值
                    sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
                            .newInstance(new WebViewDelegate());
                    if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                    return sProviderInstance;
                } catch (Exception e) {
                    Log.e(LOGTAG, "error instantiating provider", e);
                    throw new AndroidRuntimeException(e);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                StrictMode.setThreadPolicy(oldPolicy);
            }
        }
    }

异常信息就是在注释1处抛出的,如果是系统id,就抛出异常。要解决该问题,绕开该条件判断就可以了。如果可以修改系统源码,去掉改判断,返回sProviderInstance 就可以。如何在不修改系统源码的前提下,在app端修改就解决这个问题呢?

通过源码我们知道 ,当sProviderInstance 不为空时,是不会再执行下去的,直接返回sProviderInstance。那么我们可以通过反射,在调用webview 之前,给sProviderInstance 赋值,这样就可以了。

注释3处就是 sProviderInstance 赋值的方式。 反射方法如下

public static void hock(){
        int sdkVersion = Build.VERSION.SDK_INT;
        Log.d(TAG, "hock-->sdkVersion:"+sdkVersion);
        try {
    
            Class factoryClass = Class.forName("android.webkit.WebViewFactory");
            Field field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            Object sProviderInstance = field.get(null);
            if (sProviderInstance!=null){
                Log.d(TAG, "hock: sProviderInstance is not null");
                return;
            }
Method getProviderMethod = null;
            if(sdkVersion>22){
                getProviderMethod = factoryClass.getDeclaredMethod("getProviderClass");
            }else if(sdkVersion==22){
                getProviderMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            }else {
                Log.d(TAG, "hock: it is not need to hock webview!");
                return;
            }
            getProviderMethod.setAccessible(true);
            Class providerClass = (Class) getProviderMethod.invoke(factoryClass);
            Class delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor providerConstructor = providerClass.getConstructor(delegateClass);
if(providerConstructor!=null){
                providerConstructor.setAccessible(true);
                Constructor constructor = delegateClass.getDeclaredConstructor();
                constructor.setAccessible(true);
                sProviderInstance = providerConstructor.newInstance(constructor.newInstance());
                Log.d(TAG, "hock--> sProviderInstance"+sProviderInstance);
                field.set("sProviderInstance",sProviderInstance);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

在调用webview 之前,调用该方法就可以了。

你可能感兴趣的:(Android 系统应用使用webview 报错)