之前的博客介绍了chromium代码的下载和编译,调试环境的搭建。接下来我们根据编译的WebViewInstrumentation.apk来梳理浏览器的入口,看看chromium demo apk的启动流程。
从日志上看WebViewInstrumentation.apk 的activity是:org.chromium.android_webview.shell/.AwShellActivity。我们看看webview apk的启动流程。
当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。通常们是不需要指定一个Application的,系统会自动帮我们创建,WebViewInstrumentation apk在AndroidManifest.xml 文件中的application标签添加了AwShellApplication。所以我们先看看AwShellApplication.java都做了什么,代码如下:
public class AwShellApplication extends Application {
// Called by the framework for ALL processes. Runs before ContentProviders are created.
// Quirk: context.getApplicationContext() returns null during this method.
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
ContextUtils.initApplicationContext(this);
PathUtils.setPrivateDataDirectorySuffix("webview", "WebView");
CommandLine.initFromFile("/data/local/tmp/android-webview-command-line");
ResourceBundle.setAvailablePakLocales(
new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales());
}
}
这部分的实现都是在base目录下,这里都是一些初始化的操作,这里也记住下commoand读取的路径。好了接着看AwShellActivity.java onCreate执行了哪些操作:
1 首先调用registerResources(this),其实现就是调用getResources获取resource,然后存储在AwResource全局变量sResources中, 设置Uuid存储在AwResource就没干啥了;
2 AwBrowserProcess.loadLibrary(null) ,加载库;
3 判断/data/local/tmp/android-webview-command-line是否含trace命令,如果有则设置trace enable;
4 createAwTestContainerView,可以看到其实现先调用AwBrowserProcess.start()起browser进程,new AwTestContainerView设置硬件渲染,new AwSettings部分参数设置,new AwDevToolsServer,将devtool调试开关设置为开;
5 然后是获取NavigationController,设置焦点、URL初始化,前进、后退安静初始化,加载URL
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AwShellResourceProvider.registerResources(this);
AwBrowserProcess.loadLibrary(null);
if (CommandLine.getInstance().hasSwitch(AwShellSwitches.ENABLE_ATRACE)) {
Log.e(TAG, "Enabling Android trace.");
TraceEvent.setATraceEnabled(true);
}
setContentView(R.layout.testshell_activity);
mAwTestContainerView = createAwTestContainerView();
mWebContents = mAwTestContainerView.getWebContents();
mNavigationController = mWebContents.getNavigationController();
LinearLayout contentContainer = (LinearLayout) findViewById(R.id.content_container);
mAwTestContainerView.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1f));
contentContainer.addView(mAwTestContainerView);
mAwTestContainerView.requestFocus();
initializeUrlField();
initializeNavigationButtons();
String startupUrl = getUrlFromIntent(getIntent());
if (TextUtils.isEmpty(startupUrl)) {
startupUrl = INITIAL_URL;
}
mAwTestContainerView.getAwContents().loadUrl(startupUrl);
AwContents.setShouldDownloadFavicons();
mUrlTextView.setText(startupUrl);
}
代码路径:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
接下来看看上面1~5的实现:
1、registerResources的实现,并没什么复杂的操作;
public static void registerResources(Context context) {
if (sInitialized) {
return;
}
AwResource.setResources(context.getResources());
AwResource.setConfigKeySystemUuidMapping(R.array.config_key_system_uuid_mapping);
sInitialized = true;
}
代码路径:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellResourceProvider.java
2、AwBrowserProcess.loadLibrary(null) ,加载库
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);
}
}
代码路径:./android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
如果想跟整个so的加载流程,可以在loadWithSystemLinkerAlreadyLocked函数中添加堆栈,查看整个调用流程
private void loadWithSystemLinkerAlreadyLocked(ApplicationInfo appInfo, boolean inZygote) {
setEnvForNative();
preloadAlreadyLocked(appInfo, inZygote);
// If the libraries are located in the zip file, assert that the device API level is M or
// higher. On devices <=M, the libraries should always be loaded by LegacyLinker.
assert !isInZipFile() || Build.VERSION.SDK_INT >= VERSION_CODES.M;
// Load libraries using the system linker.
for (String library : NativeLibraries.LIBRARIES) {
if (!isInZipFile()) {
System.loadLibrary(library);
} else {
// Load directly from the APK.
boolean is64Bit = ApiHelperForM.isProcess64Bit();
String zipFilePath = appInfo.sourceDir;
boolean crazyPrefix = forceSystemLinker(); // See comment in this function.
String fullPath = zipFilePath + "!/"
+ makeLibraryPathInZipFile(library, crazyPrefix, is64Bit);
Log.i(TAG, "libraryName: %s", fullPath);
System.load(fullPath);
}
}
}
代码路径:./base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
3 判断/data/local/tmp/android-webview-command-line是否含trace命令,如果有则设置trace enable
public static void setATraceEnabled(boolean enabled) {
if (sATraceEnabled == enabled) return;
sATraceEnabled = enabled;
if (enabled) {
// Calls TraceEvent.setEnabled(true) via
// TraceLog::EnabledStateObserver::OnTraceLogEnabled
TraceEventJni.get().startATrace();
} else {
// Calls TraceEvent.setEnabled(false) via
// TraceLog::EnabledStateObserver::OnTraceLogDisabled
TraceEventJni.get().stopATrace();
}
}
代码路径:./base/android/java/src/org/chromium/base/TraceEvent.java
4 createAwTestContainerView的函数实现如下
private AwTestContainerView createAwTestContainerView() {
AwBrowserProcess.start();
AwTestContainerView testContainerView = new AwTestContainerView(this, true);
......
SharedPreferences sharedPreferences =
getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
if (mBrowserContext == null) {
mBrowserContext = new AwBrowserContext(
sharedPreferences, AwBrowserContext.getDefault().getNativePointer(), true);
}
final AwSettings awSettings =
new AwSettings(this /* context */, false /* isAccessFromFileURLsGrantedByDefault */,
false /* supportsLegacyQuirks */, false /* allowEmptyDocumentPersistence */,
true /* allowGeolocationOnInsecureOrigins */,
false /* doNotUpdateSelectionOnMutatingSelectionRange */);
// Required for WebGL conformance tests.
awSettings.setMediaPlaybackRequiresUserGesture(false);
// Allow zoom and fit contents to screen
awSettings.setBuiltInZoomControls(true);
awSettings.setDisplayZoomControls(false);
awSettings.setUseWideViewPort(true);
awSettings.setLoadWithOverviewMode(true);
awSettings.setLayoutAlgorithm(AwSettings.LAYOUT_ALGORITHM_TEXT_AUTOSIZING);
testContainerView.initialize(new AwContents(mBrowserContext, testContainerView,
testContainerView.getContext(), testContainerView.getInternalAccessDelegate(),
testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings));
testContainerView.getAwContents().getSettings().setJavaScriptEnabled(true);
if (mDevToolsServer == null) {
mDevToolsServer = new AwDevToolsServer();
mDevToolsServer.setRemoteDebuggingEnabled(true);
}
return testContainerView;
}
4.1 我们先来看如何browser线程,看start的实现如下,调用了startBrowserProcessesSync函数
public static void start() {
try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwBrowserProcess.start")) {
final Context appContext = ContextUtils.getApplicationContext();
AwDataDirLock.lock(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(() -> {
boolean multiProcess =
CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
if (multiProcess) {
ChildProcessLauncherHelper.warmUp(appContext, true);
}
// The policies are used by browser startup, so we need to register the policy
// providers before starting the browser process. This only registers java objects
// and doesn't need the native library.
CombinedPolicyProvider.get().registerProvider(new AwPolicyProvider(appContext));
// Check android settings but only when safebrowsing is enabled.
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("AwBrowserProcess.maybeEnable")) {
AwSafeBrowsingConfigHelper.maybeEnableSafeBrowsingFromManifest(appContext);
}
try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
"AwBrowserProcess.startBrowserProcessesSync")) {
BrowserStartupController.getInstance().startBrowserProcessesSync(
LibraryProcessType.PROCESS_WEBVIEW, !multiProcess);
}
});
}
}
代码路径:./android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
startBrowserProcessesSync 实现代码如下,通过调用contentStart函数,调用ContentMain.start()函数:
public void startBrowserProcessesSync(
@LibraryProcessType int libraryProcessType, boolean singleProcess) {
......
if (contentStart() > 0) {
// Failed. The callbacks may not have run, so run them.
enqueueCallbackExecution(STARTUP_FAILURE);
startedSuccessfully = false;
}
......
}
代码路径:./content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
ContentMain.start函数实现如下,往下调用的函数是jni函数nativeStart:
public static int start(boolean startServiceManagerOnly) {
return ContentMainJni.get().start(startServiceManagerOnly);
}
代码路径:./content/public/android/java/src/org/chromium/content/app/ContentMain.java
接下来我们看看nativeStart在c++做啥,函数实现如下,调用service_manager Main;
static jint JNI_ContentMain_Start(JNIEnv* env,
jboolean start_service_manager_only) {
TRACE_EVENT0("startup", "content::Start");
DCHECK(!g_service_manager_main_delegate.Get() || !start_service_manager_only);
if (!g_service_manager_main_delegate.Get()) {
g_service_manager_main_delegate.Get() =
std::make_unique(
ContentMainParams(g_content_main_delegate.Get().get()));
}
static_cast(
g_service_manager_main_delegate.Get().get())
->SetStartServiceManagerOnly(start_service_manager_only);
service_manager::MainParams main_params(
g_service_manager_main_delegate.Get().get());
return service_manager::Main(main_params);
}
代码路径:./content/app/android/content_main.cc
接着往下看看service_manager Main做了什么,做了mojo的初始然,然后是通过delegate->RunEmbedderProcess()调用content_service_manager_main_delegate.cc的RunEmbedderProcess。
int Main(const MainParams& params) {
MainDelegate* delegate = params.delegate;
......
int exit_code = -1;
base::debug::GlobalActivityTracker* tracker = nullptr;
ProcessType process_type = delegate->OverrideProcessType();
static bool is_initialized = false;
if (!is_initialized) {
is_initialized = true;
......
mojo::core::Configuration mojo_config;
if (process_type == ProcessType::kDefault &&
command_line.GetSwitchValueASCII(switches::kProcessType) ==
switches::kProcessTypeServiceManager) {
mojo_config.is_broker_process = true;
}
mojo_config.max_message_num_bytes = kMaximumMojoMessageSize;
delegate->OverrideMojoConfiguration(&mojo_config);
mojo::core::Init(mojo_config);
ui::RegisterPathProvider();
tracker = base::debug::GlobalActivityTracker::Get();
exit_code = delegate->Initialize(init_params);
if (exit_code >= 0) {
if (tracker) {
tracker->SetProcessPhase(
base::debug::GlobalActivityTracker::PROCESS_LAUNCH_FAILED);
tracker->process_data().SetInt("exit-code", exit_code);
}
return exit_code;
}
......
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTraceToConsole)) {
base::trace_event::TraceConfig trace_config =
tracing::GetConfigForTraceToConsole();
base::trace_event::TraceLog::GetInstance()->SetEnabled(
trace_config, base::trace_event::TraceLog::RECORDING_MODE);
}
}
const auto& command_line = *base::CommandLine::ForCurrentProcess();
if (process_type == ProcessType::kDefault) {
std::string type_switch =
command_line.GetSwitchValueASCII(switches::kProcessType);
if (type_switch == switches::kProcessTypeServiceManager) {
process_type = ProcessType::kServiceManager;
} else if (type_switch == switches::kProcessTypeService) {
process_type = ProcessType::kService;
} else {
process_type = ProcessType::kEmbedder;
}
}
switch (process_type) {
case ProcessType::kDefault:
NOTREACHED();
break;
case ProcessType::kServiceManager:
exit_code = RunServiceManager(delegate);
break;
case ProcessType::kService:
CommonSubprocessInit();
exit_code = RunService(delegate);
break;
case ProcessType::kEmbedder:
if (delegate->IsEmbedderSubprocess())
CommonSubprocessInit();
exit_code = delegate->RunEmbedderProcess();
break;
}
if (tracker) {
if (exit_code == 0) {
tracker->SetProcessPhaseIfEnabled(
base::debug::GlobalActivityTracker::PROCESS_EXITED_CLEANLY);
} else {
tracker->SetProcessPhaseIfEnabled(
base::debug::GlobalActivityTracker::PROCESS_EXITED_WITH_CODE);
tracker->process_data().SetInt("exit-code", exit_code);
}
}
......
if (process_type == ProcessType::kEmbedder)
delegate->ShutDownEmbedderProcess();
return exit_code;
}
代码路径:./services/service_manager/embedder/main.cc
然后可以看content_main_runner_impl.cc的Run函数,调用RegisterMainThreadFactories,创建InProcessUtility、InProcessRenderer、InProcessGpu线程并注册。然后是调用RunServiceManager,然后是创建起来的相关任务,我在browser_main_loop.cc的CreateStartupTasks函数中添加了堆栈,方便看流程。
int ContentMainRunnerImpl::Run(bool start_service_manager_only) {
......
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line.GetSwitchValueASCII(switches::kProcessType);
// Run this logic on all child processes. Zygotes will run this at a later
// point in time when the command line has been updated.
if (!process_type.empty() &&
process_type != service_manager::switches::kZygoteProcess) {
InitializeFieldTrialAndFeatureList();
delegate_->PostFieldTrialInitialization();
}
......
RegisterMainThreadFactories();
if (process_type.empty()){
return RunServiceManager(main_params, start_service_manager_only);
}
return RunOtherNamedProcessTypeMain(process_type, main_params, delegate_);
}
代码路径:./content/app/content_main_runner_impl.cc
堆栈打印的流程如下,接着往下可以看看都创建了什么任务:
./../../content/browser/browser_main_loop.cc:899 BrowserMainLoop::CreateStartupTasks
./../../content/browser/browser_main_runner_impl.cc:129 main_loop_->CreateStartupTasks();
./../../android_webview/lib/aw_main_delegate.cc:307 int exit_code = browser_runner_->Initialize(main_function_params);
./../../content/app/content_main_runner_impl.cc:502 int exit_code = delegate->RunProcess("", main_function_params);
./../../content/app/content_main_runner_impl.cc:950 return RunBrowserProcessMain(main_params, delegate_);
./../../content/app/content_main_runner_impl.cc:851 return RunServiceManager(main_params, start_service_manager_only);
./../../services/service_manager/embedder/main.cc:459 exit_code = delegate->RunEmbedderProcess();
./../../content/app/android/content_main.cc:62 return service_manager::Main(main_params);
4.2 然后是DevToolsServer对象的新建,devtool不太了解的小伙伴可以看看这个链接,devtool sever的开启便于后面的调试 https://developers.google.com/web/tools/chrome-devtools。这里只介绍如何启动devtool server,不介绍devtool server 如何实现。
if (mDevToolsServer == null) {
mDevToolsServer = new AwDevToolsServer();
mDevToolsServer.setRemoteDebuggingEnabled(true);
}
public void setRemoteDebuggingEnabled(boolean enabled) {
AwDevToolsServerJni.get().setRemoteDebuggingEnabled(
AwDevToolsServer.this, mNativeDevToolsServer, enabled);
}
static void JNI_AwDevToolsServer_SetRemoteDebuggingEnabled(
JNIEnv* env,
const JavaParamRef& obj,
jlong server,
jboolean enabled) {
AwDevToolsServer* devtools_server =
reinterpret_cast(server);
if (enabled) {
devtools_server->Start();
} else {
devtools_server->Stop();
}
}
代码路径:./android_webview/browser/aw_devtools_server.cc
5 我们来看看URL的加载mAwTestContainerView.getAwContents().loadUrl(startupUrl);兜兜转转会调用到NavigationControllerImpl.java的loadUrl函数,调用一个JNI又是到C++了,调用的是navigation_controller_impl.cc的LoadURL,后面资源加载部分,再对整个请求流程进行分析。
public void loadUrl(LoadUrlParams params) {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get().loadUrl(mNativeNavigationControllerAndroid,
NavigationControllerImpl.this, params.getUrl(), params.getLoadUrlType(),
params.getTransitionType(),
params.getReferrer() != null ? params.getReferrer().getUrl() : null,
params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
params.getUserAgentOverrideOption(), params.getExtraHeadersString(),
params.getPostData(), params.getBaseUrl(), params.getVirtualUrlForDataUrl(),
params.getDataUrlAsString(), params.getCanLoadLocalResources(),
params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry());
}
}
代码路径:./content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
往下就是开始资源加载了,在下篇博客中进行更新