Android webview
Android WebView加载Chromium动态库的过程分析
android系统对WebView控件的实现进行了修改:
- 在WebView控件和浏览器内核之间加入了一层代理层。以屏蔽浏览器内核的差异。目前支持WebKit和Chrome两种内核。
- 在frameworks/webview目录下新增两个package,一个为webkit,一个为chrome。
- 代理层通过配置来加载这两个package中间的一个,对浏览器内核进行选择。
- 具体选用哪个浏览器内核,有com.internal.R.string.config_webViewPackageName这个字符串指定的包名来决定。
在5.0中,com.internal.R.string.config_webViewPackageName的值被获取出来是: com.google.android.webview,也就是chrome内核。在5.1以及以上的版本中,com.internal.R.string.config_webViewPackageName的值被获取出来是: com.android.webview, 也就是webkit内核。
首先要了解webview的加载过程,主要还是这一步
initialApplication.getAssets().addAssetPathAsSharedLibrary(
webViewContext.getApplicationInfo().sourceDir);
webview加载chrome内核的apk,把相应的资源文件通过AssetManager加载到当前的Resources中, 而现在的报错的原因应该是没有加载成功,具体什么原因还不清楚
private static Class<WebViewFactoryProvider> getProviderClass() {
Context webViewContext = null;
Application initialApplication = AppGlobals.getInitialApplication();
try {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"WebViewFactory.getWebViewContextAndSetProvider()");
try {
webViewContext = getWebViewContextAndSetProvider();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
initialApplication.getAssets().addAssetPathAsSharedLibrary(
webViewContext.getApplicationInfo().sourceDir);
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
loadNativeLibrary(clazzLoader, sPackageInfo);
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
try {
return getWebViewProviderClass(clazzLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
Log.e(LOGTAG, "error loading provider", e);
throw new AndroidRuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (MissingWebViewPackageException e) {
// If the package doesn't exist, then try loading the null WebView instead.
// If that succeeds, then this is a device without WebView support; if it fails then
// swallow the failure, complain that the real WebView is missing and rethrow the
// original exception.
try {
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
} catch (ClassNotFoundException e2) {
// Ignore.
}
Log.e(LOGTAG, "Chromium WebView package does not exist", e);
throw new AndroidRuntimeException(e);
}
}
解决方案:
在插件启动时预先加载webview所依赖的内核apk的资源文件,这样webview在插件进程内访问资源文件时就不会报错了,这块实际上和实现插件化的原理相同
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Build;
import android.text.TextUtils;
import java.lang.reflect.Method;
public class WebViewResourceHelper {
private static boolean sInitialed = false;
public static boolean addChromeResourceIfNeeded(Context context) {
if (sInitialed) {
return true;
}
String resourceDir = getWebViewResourceDir(context);
if (TextUtils.isEmpty(resourceDir)) {
return false;
}
try {
Method m = getAddAssetPathMethod();
if (m != null) {
int ret = (int) m.invoke(context.getAssets(), resourceDir);
sInitialed = ret > 0;
return sInitialed;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private static Method getAddAssetPathMethod() {
Method m = null;
Class c = AssetManager.class;
if (Build.VERSION.SDK_INT >= 24) {
try {
m = c.getDeclaredMethod("addAssetPathAsSharedLibrary", String.class);
m.setAccessible(true);
} catch (NoSuchMethodException e) {
// Do Nothing
e.printStackTrace();
}
return m;
}
try {
m = c.getDeclaredMethod("addAssetPath", String.class);
m.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return m;
}
private static String getWebViewResourceDir(Context context) {
String pkgName = getWebViewPackageName();
if (TextUtils.isEmpty(pkgName)) {
return null;
}
try {
PackageInfo pi = context.getPackageManager().getPackageInfo(getWebViewPackageName(), PackageManager.GET_SHARED_LIBRARY_FILES);
return pi.applicationInfo.sourceDir;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String getWebViewPackageName() {
int sdk = Build.VERSION.SDK_INT;
if (sdk <= 20) {
return null;
}
switch (sdk) {
case 21:
case 22:
return getWebViewPackageName4Lollipop();
case 23:
return getWebViewPackageName4M();
case 24:
return getWebViewPackageName4N();
case 25:
default:
return getWebViewPackageName4More();
}
}
private static String getWebViewPackageName4Lollipop() {
try {
return (String) invokeStaticMethod("android.webkit.WebViewFactory", "getWebViewPackageName", null);
} catch (Throwable e) {
e.printStackTrace();
}
return "com.google.android.webview";
}
private static String getWebViewPackageName4M() {
return getWebViewPackageName4Lollipop();
}
private static String getWebViewPackageName4N() {
try {
Context c = (Context) invokeStaticMethod("android.webkit.WebViewFactory", "getWebViewContextAndSetProvider", null);
return c.getApplicationInfo().packageName;
} catch (Throwable e) {
e.printStackTrace();
}
return "com.google.android.webview";
}
private static String getWebViewPackageName4More() {
return getWebViewPackageName4N();
}
public static Object invokeStaticMethod(String clzName, String methodName, Class<?>[] methodParamTypes, Object... methodParamValues) {
try {
Class clz = Class.forName(clzName);
if (clz != null) {
Method method = clz.getDeclaredMethod(methodName, methodParamTypes);
if (method != null) {
method.setAccessible(true);
return method.invoke(null, methodParamValues);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
————————————————
转载于:https://blog.csdn.net/u012359498/article/details/106660297/