我们在使用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 之前,调用该方法就可以了。