从前面 webview创建过程分析(一)中我们知道在webview创建过程中会创建WebViewChormiumFactoryProvider和WebViewChromium,今天我们来分析下这两个创建过程的源码。
WebViewChormiumFactoryProvider创建过程
public static WebViewChromiumFactoryProvider create(android.webkit.WebViewDelegate delegate) {
return new WebViewChromiumFactoryProvider(delegate);
}
从上面的代码可以知道要创建WebViewChromiumFactoryProvider,需要WebViewDelegate,这个delegate是在new WebView的时候创建的。
public WebViewChromiumFactoryProvider(android.webkit.WebViewDelegate delegate) {
initialize(WebViewDelegateFactory.createProxyDelegate(delegate));
}
这里会先调用WebViewDelegateFactory的createProxyDelegate方法将客户端的WebViewDelegate的类转换成chromium中的WebViewDelegate,然后调用initialize方法
@SuppressWarnings("NoContextGetApplicationContext")
private void initialize(WebViewDelegate webViewDelegate) {
long startTime = SystemClock.elapsedRealtime();
try (ScopedSysTraceEvent e1 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.initialize")) {
PackageInfo packageInfo;
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.getLoadedPackageInfo")) {
// The package is used to locate the services for copying crash minidumps and
// requesting variations seeds. So it must be set before initializing variations and
// before a renderer has a chance to crash.
packageInfo = WebViewFactory.getLoadedPackageInfo();
}
AwBrowserProcess.setWebViewPackageName(packageInfo.packageName);
//1.创建WebViewChromiumAwInit对象
mAwInit = createAwInit();
mWebViewDelegate = webViewDelegate;
Context ctx = webViewDelegate.getApplication().getApplicationContext();
// If the application context is DE, but we have credentials, use a CE context instead
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.checkStorage")) {
checkStorageIsNotDeviceProtected(webViewDelegate.getApplication());
} catch (IllegalArgumentException e) {
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
if (!GlueApiHelperForN.isUserUnlocked(ctx)) {
throw e;
}
ctx = GlueApiHelperForN.createCredentialProtectedStorageContext(ctx);
}
// WebView needs to make sure to always use the wrapped application context.
ctx = ClassLoaderContextWrapperFactory.get(ctx);
ContextUtils.initApplicationContext(ctx);
// Find the package ID for the package that WebView's resources come from.
// This will be the donor package if there is one, not our main package.
String resourcePackage = packageInfo.packageName;
if (packageInfo.applicationInfo.metaData != null) {
resourcePackage = packageInfo.applicationInfo.metaData.getString(
"com.android.webview.WebViewDonorPackage", resourcePackage);
}
int packageId = webViewDelegate.getPackageId(ctx.getResources(), resourcePackage);
mAwInit.setUpResourcesOnBackgroundThread(packageId, ctx);
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.initCommandLine")) {
// This may take ~20 ms only on userdebug devices.
CommandLineUtil.initCommandLine();
}
boolean multiProcess = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Ask the system if multiprocess should be enabled on O+.
multiProcess = webViewDelegate.isMultiProcessEnabled();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Check the multiprocess developer setting directly on N.
multiProcess = Settings.Global.getInt(ctx.getContentResolver(),
Settings.Global.WEBVIEW_MULTIPROCESS, 0)
== 1;
}
//2.设置是否是多进程
if (multiProcess) {
CommandLine cl = CommandLine.getInstance();
cl.appendSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
}
int applicationFlags = ctx.getApplicationInfo().flags;
boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isOsDebuggable = BuildInfo.isDebugAndroid();
// Enable logging JS console messages in system logs only if the app is debuggable or
// it's a debugable android build.
if (isAppDebuggable || isOsDebuggable) {
CommandLine cl = CommandLine.getInstance();
cl.appendSwitch(AwSwitches.WEBVIEW_LOG_JS_CONSOLE_MESSAGES);
}
ThreadUtils.setWillOverrideUiThread(true);
BuildInfo.setBrowserPackageInfo(packageInfo);
//3.加载相关so
try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.loadChromiumLibrary")) {
String dataDirectorySuffix = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
dataDirectorySuffix = webViewDelegate.getDataDirectorySuffix();
}
AwBrowserProcess.loadLibrary(dataDirectorySuffix);
}
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.loadGlueLayerPlatSupportLibrary")) {
System.loadLibrary("webviewchromium_plat_support");
}
deleteContentsOnPackageDowngrade(packageInfo);
}
// Now safe to use WebView data directory.
mAwInit.startVariationsInit();
mShouldDisableThreadChecking = shouldDisableThreadChecking(ctx);
//4.设置单例
setSingleton(this);
}
TimesHistogramSample histogram =
new TimesHistogramSample("Android.WebView.Startup.CreationTime.Stage1.FactoryInit");
histogram.record(SystemClock.elapsedRealtime() - startTime);
}
initialize方法主要执行几个步骤
- 创建 WebViewChromiumAwInit对象
- 设置多进程相关信息
- 加载so
- 设置自己为单例
我们看下这几步执行的一些具体内容
protected WebViewChromiumAwInit createAwInit() {
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.createAwInit")) {
return new WebViewChromiumAwInit(this);
}
}
WebViewChromiumAwInit(WebViewChromiumFactoryProvider factory) {
mFactory = factory;
// Do not make calls into 'factory' in this ctor - this ctor is called from the
// WebViewChromiumFactoryProvider ctor, so 'factory' is not properly initialized yet.
}
这里主要就是new了一个WebViewChromiumAwInit对象,同时为mFactory赋值,指向的是一个WebViewChromiumFactoryProvider对象。
- 设置多进程相关信息
这里会通过CommandLine对象将多进程相关的信息存起来,CommandLine是单例模式的
- 加载so
主要通过AwBrowserProcess来加载so
public static void loadLibrary(String processDataDirSuffix) {
LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_WEBVIEW);
if (processDataDirSuffix == null) {
PathUtils.setPrivateDataDirectorySuffix(WEBVIEW_DIR_BASENAME, "WebView");
} else {
String processDataDirName = WEBVIEW_DIR_BASENAME + "_" + processDataDirSuffix;
PathUtils.setPrivateDataDirectorySuffix(processDataDirName, processDataDirName);
}
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
LibraryLoader.getInstance().loadNow();
// Switch the command line implementation from Java to native.
// It's okay for the WebView to do this before initialization because we have
// setup the JNI bindings by this point.
LibraryLoader.getInstance().switchCommandLineForWebView();
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
上面就是WebViewChormiumFactoryProvider创建过程中涉及到的一些逻辑
WebViewChromium创建过程
public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
WebView.PrivateAccess webViewPrivate, boolean shouldDisableThreadChecking) {
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("WebViewChromium.constructor")) {
WebViewChromiumFactoryProvider.checkStorageIsNotDeviceProtected(webView.getContext());
mWebView = webView;
mWebViewPrivate = webViewPrivate;
mHitTestResult = new WebView.HitTestResult();
mContext = ClassLoaderContextWrapperFactory.get(mWebView.getContext());
mAppTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mFactory = factory;
mShouldDisableThreadChecking = shouldDisableThreadChecking;
factory.getWebViewDelegate().addWebViewAssetPath(mWebView.getContext());
mSharedWebViewChromium =
new SharedWebViewChromium(mFactory.getRunQueue(), mFactory.getAwInit());
}
}
这里主要是为一些成员变量赋值,同时会创建SharedWebViewChromium对象
public SharedWebViewChromium(WebViewChromiumRunQueue runQueue, WebViewChromiumAwInit awInit) {
mRunQueue = runQueue;
mAwInit = awInit;
}
SharedWebViewChromium对象的创建过程主要是为两个成员mRunQueue和mAwInit赋值。其中mAwInit对象就是在前面 WebViewChormiumFactoryProvider的创建过程中创建的,mRunQueue是通过mFactory.getRunQueue来赋值的。我们看下这个WebViewChromiumRunQueue是干什么的
private final WebViewChromiumRunQueue mRunQueue = new WebViewChromiumRunQueue(
() -> { return WebViewChromiumFactoryProvider.this.mAwInit.hasStarted(); });
/* package */ WebViewChromiumRunQueue getRunQueue() {
return mRunQueue;
}
public WebViewChromiumRunQueue(ChromiumHasStartedCallable chromiumHasStartedCallable) {
mQueue = new ConcurrentLinkedQueue<Runnable>();
mChromiumHasStartedCallable = chromiumHasStartedCallable;
}
这里主要就是创建一个WebViewChromiumRunQueue,里面有个包含Runnable的队列。
以上就是 WebViewChromium创建过程中的一些执行逻辑。
WebViewChromium初始化过程
前面webview创建过程分析(一)中我们知道,在new WebView的过程中会调用WebViewChromium的init方法,我们看下在init过程中都执行了什么逻辑。
@Override
// BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
// so is ignored. TODO: remove it from WebViewProvider.
public void init(final Map<String, Object> javaScriptInterfaces,
final boolean privateBrowsing) {
long startTime = SystemClock.elapsedRealtime();
boolean isFirstWebViewInit = !mFactory.hasStarted();
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("WebViewChromium.init")) {
if (privateBrowsing) {
mFactory.startYourEngines(true);
final String msg = "Private browsing is not supported in WebView.";
if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
throw new IllegalArgumentException(msg);
} else {
Log.w(TAG, msg);
TextView warningLabel = new TextView(mContext);
warningLabel.setText(mContext.getString(
org.chromium.android_webview.R.string.private_browsing_warning));
mWebView.addView(warningLabel);
}
}
// We will defer real initialization until we know which thread to do it on, unless:
// - we are on the main thread already (common case),
// - the app is targeting >= JB MR2, in which case checkThread enforces that all usage
// comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//1.启动browser进程
mFactory.startYourEngines(false);
checkThread();
} else if (!mFactory.hasStarted()) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mFactory.startYourEngines(true);
}
}
final boolean isAccessFromFileURLsGrantedByDefault =
mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
final boolean areLegacyQuirksEnabled =
mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
final boolean allowEmptyDocumentPersistence =
mAppTargetSdkVersion <= Build.VERSION_CODES.M;
final boolean allowGeolocationOnInsecureOrigins =
mAppTargetSdkVersion <= Build.VERSION_CODES.M;
// https://crbug.com/698752
final boolean doNotUpdateSelectionOnMutatingSelectionRange =
mAppTargetSdkVersion <= Build.VERSION_CODES.M;
//2.创建ConetntsClientAdapter
mContentsClientAdapter =
mFactory.createWebViewContentsClientAdapter(mWebView, mContext);
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("WebViewChromium.ContentSettingsAdapter")) {
//3.创建WebSettings
mWebSettings = mFactory.createCo