今天我们来看下WebView的Browser端的启动过程,在前面webview创建过程分析(二)的分析中,我们知道WebViewChromium在init过程中会涉及到Browser端的启动,具体代码为
public void init(final Map<String, Object> javaScriptInterfaces,
final boolean privateBrowsing) {
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(mWebView.getContext());
warningLabel.setText(mWebView.getContext().getString(
com.android.internal.R.string.webviewchromium_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);
}
}
......
}
今天我们就来分析这里Browser的启动逻辑,从上面的代码可以知道这里会调用WebViewChromiumFactoryProvider的startYourEngines方法,
void startYourEngines(boolean onMainThread) {
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.startYourEngines")) {
mAwInit.startYourEngines(onMainThread);
}
}
这里我们假设webview的创建是在主线程进行的,即这里的onMainThread为true,接着调用
void startYourEngines(boolean onMainThread) {
synchronized (mLock) {
ensureChromiumStartedLocked(onMainThread);
}
}
void ensureChromiumStartedLocked(boolean onMainThread) {
assert Thread.holdsLock(mLock);
if (mStarted) { // Early-out for the common case.
return;
}
Looper looper = !onMainThread ? Looper.myLooper() : Looper.getMainLooper();
Log.v(TAG, "Binding Chromium to "
+ (Looper.getMainLooper().equals(looper) ? "main" : "background")
+ " looper " + looper);
//1.设置uiThread
ThreadUtils.setUiThread(looper);
if (ThreadUtils.runningOnUiThread()) {
startChromiumLocked();
return;
}
// We must post to the UI thread to cover the case that the user has invoked Chromium
// startup by using the (thread-safe) CookieManager rather than creating a WebView.
PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
@Override
public void run() {
synchronized (mLock) {
startChromiumLocked();
}
}
});
while (!mStarted) {
try {
// Important: wait() releases |mLock| the UI thread can take it :-)
mLock.wait();
} catch (InterruptedException e) {
// Keep trying... eventually the UI thread will process the task we sent it.
}
}
}
由于webview在主线程中创建,所以这里的looper就是主线程的Looper,下面看下ThreadUtils.setUiThread的实现逻辑。
public static void setUiThread(Looper looper) {
synchronized (sLock) {
if (looper == null) {
// Used to reset the looper after tests.
sUiThreadHandler = null;
return;
}
if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) {
throw new RuntimeException("UI thread looper is already set to "
+ sUiThreadHandler.getLooper() + " (Main thread looper is "
+ Looper.getMainLooper() + "), cannot set to new looper " + looper);
} else {
sUiThreadHandler = new Handler(looper);
}
}
}
这里创建了一个Handler,名为sUiThreadHandler,它的Looper就是主线程的Looper。
接下来就是调用startChormiumLocked方法
protected void startChromiumLocked() {
try (ScopedSysTraceEvent event =
ScopedSysTraceEvent.scoped("WebViewChromiumAwInit.startChromiumLocked")) {
assert Thread.holdsLock(mLock) && ThreadUtils.runningOnUiThread();
// The post-condition of this method is everything is ready, so notify now to cover all
// return paths. (Other threads will not wake-up until we release |mLock|, whatever).
mLock.notifyAll();
if (mStarted) {
return;
}
......
AwBrowserProcess.start();
......
mStarted = true;
}
}
这里会先判断之前有没有启动过,有的话就返回,没有就调用AwBrwoserProcess.start,然后标记为已启动,看下start的代码实现
public static void start() {
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwBrowserProcess.start")) {
final Context appContext = ContextUtils.getApplicationContext();
tryObtainingDataDirLock(appContext);
// We must post to the UI thread to cover the case that the user
// has invoked Chromium startup by using the (thread-safe)
// CookieManager rather than creating a WebView.
ThreadUtils.runOnUiThreadBlocking(() -> {
//1.这里判断有没有设置多进程的属性,在android这里是单进程架构
boolean multiProcess =
CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
if (multiProcess) {