1.問題起源
報錯語句是:java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes
因難以避免WebView存在安全漏洞,系統遭受攻擊,Android不允許特權進程應用使用WebView。如果使用了,便會拋出以上異常。
特權進程包括sharedUserId為ROOT_UID和SYSTEM_UID的進程,從Android O(8.0)開始共享PHONE_UID、NFC_UID、BLUETOOTH_UID的進程也屬於特權進程。
如果不幸,你的應用共享了以上的UserId,又有使用WebView,將會Crash掉,並拋出異常。下面提供一種Hook代碼的方法,可以不用修改sharedUserId且避免Crash。
不過為了安全起見,還是不建議在Google規定的特權進程里繼續使用WebView。下面提供的方案僅供不方便很快去掉WebView,臨時避免Crash使用。
2.解決方法
請自行搜索了解為什么會想起這樣解決,我是分析了Android8.0 frameworks源碼,對網上的方法進行了改進,以使其在Android 8.0(SDK版本號為26)上使用.
在你的Application OnCreate方法里調用下面的方法即可。
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) {
Log.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 {
Log.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);
if(sdkInt < 26){//低於Android O版本
Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
if (providerConstructor != null) {
providerConstructor.setAccessible(true);
sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
}
} else {
Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
chromiumMethodName.setAccessible(true);
String chromiumMethodNameStr = (String)chromiumMethodName.get(null);
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);
Log.i(TAG,"Hook success!");
} else {
Log.i(TAG,"Hook failed!");
}
} catch (Throwable e) {
Log.w(TAG,e);
}
}