您的位置:首页 > 其它

一起学习Chromium之Browser进程启动分析

2016-08-10 13:09 2101 查看

前言

从接触Chromium开始,参考过很多网上的分析文章,获益匪浅。本系列文章尝试着从新的角度,逐步分析Chromium的部分关键知识点,希望能分析清楚初学者常常比较困惑的问题,比如Chromium怎么启动,各个进程(Browser/Render/GPU等)怎么启动,Chromium怎么显示一个网页等,进而理顺Chromium中各种类的关系和作用。而作者也会在文章中提出自己尚未理解的问题,希望能达到互动研究共同提高的效果。

ContentShell

鉴于Chromium本身的复杂和庞大,从ContentShell来分析是一个比较好的选择。关于Content模块和Chromium的关系,可以参考这里

ContentShell App的启动是从
ContentShellActivity.java
onCreate(...)
开始,这里首先会初始化CommandLine,利用CommandLine可以设置各种启动参数,比如singleprocess,GPU hardware acceleration等,这里详细列出了Chrome中支持的command lines。但这部分和本文主题无关,暂且略过。

接着会调用
LibraryLoader.java
的静态函数
get(...)
来实例化
LibraryLoader
,并指定
LibraryProcessType
PROCESS_BROWSER
。从名字可以看出来,
LibraryLoader
的目的就是加载和注册native的libraries。这是通过
ensureInitialized(...)
来调用
loadAlreadyLocked(...)
initializeAlreadyLocked(...)
来完成的。在
loadAlreadyLocked(...)
中,通过
Linker.loadLibrary(...)
或者
System.loadLibrary(...)
来触发native层的
JNI_OnLoad(...)
。ContentShell对应的native文件是
shell_library_loader.cc
,可以看到在
JNI_OnLoad(...)
中主要做了两件事情:

Compositor的初始化。

创建并设置
ContentMainDelegate
,这里是
ShellMainDelegate
.

Compositor的初始化是在
content/browser/renderer_host/compositor_impl_android.cc
Initialize()
完成的,但其实只是设置了变量
g_initialized
意义是什么?)。而
ShellMainDelegate
是比较核心的class,后面的分析会多次提到。

现在回到
ContentShellActivity.java
,会看到接下来的重点在于
BrowserStartupController
。先是通过其静态函数
get(...)
来实例化一个class,并设置其
LibraryProcessType
PROCESS_BROWSER
。这和前面的
LibraryLoader
一样,都是用了设计模式的单例模式。然后调用其
startBrowserProcessesAsync(...)
异步启动和初始化Content模块。

Tips :

startBrowserProcessesAsync(…)和startBrowserProcessesSync(…)的区别:

调用startBrowserProcessesAsync(…)会同时传入参数BrowserStartupController.StartupCallback,使得当Content模块初始化完成后,会调用ContentShellActivity类的成员函数finishInitialization(…)继续执行启动ContentShell APK的其他工作。startBrowserProcessesAsync(…)中会调用prepareToStartBrowserProcess(…),同时new Runnable()作为参数传入,目的是在随后执行contentStart()。

这里需要注意,虽然实现Runnable接口是java中两种实现多线程的方式之一(另一种是继承Thread类),但是Runnable本身和线程无关,只是一个拥有run()方法的Object。所以在
prepareToStartBrowserProcess(...)
中,会在资源提取(ResourceExtractor)完成后执行
contentStart()
。这里使用的类
ResourceExtractor
其实是和浏览器对HTML5多语言的支持有关,暂且略过。

Native层

contentStart()
最终是通过JNI调用native层
content/app/android/content_main.cc
中的
Start(...)
。而之前设置
ShellMainDelegate
正是用到了
content_main.cc
中的
SetContentMainDelegate(...)


Start(...)
中,主要做了下面几件事:

使用前面创建和设置的
ShellMainDelegate
初始化
g_content_main_delegate
,并初始化结构体
ContentMainParams  params
的成员变量
delegate


这里会用到Chromium的LazyInstance,它提供了一种延迟创建全局静态对象的方式,而且是完全的线程安全,
base/lazy_instance.h
对其有详细描述和示例。

创建并初始化
Class  ContentMainRunner(content/app/content_main_runner.cc)
的实例
g_content_runner
,初始化函数
Initialize(...)
参数正是前面创建的
params


运行
g_content_runner(ContentMainRunnerImpl)


Created with Raphaël 2.1.0初始化g_content_main_delegate创建并初始化g_content_runner运行g_content_runner结束

在上面的过程中,
Class ContentMainRunnerImpl
Initialize(...)
里面有很多重要的步骤:

delegate_->BasicStartupComplete(…)

这里的delegate_就是
g_content_main_delegate
BasicStartupComplete(…)
中会实例化
ShellContentClient
,并通过
Class ContentClient
SetContentClient(…)
设置全局变量
g_client(ShellContentClient)


ContentClientInitializer::Set(process_type, delegate_)

Class ContentClientInitializer
位于
content_main_runner.cc
中,其
Set(…)
函数分别调用
g_content_main_delegate
CreateContentBrowserClient()
CreateContentGpuClient()
CreateContentRendererClient()
CreateContentUtilityClient()
,并分别初始化
content_client
(其实就是前面的全局变量
g_client
)的变量
browser_(ShellContentBrowserClient)
gpu_(ContentGpuClient)
renderer_(ShellContentRendererClient)
utility_(ShellContentUtilityClient)


注意这里的前提条件
!define(CHROME_MULTIPLE_DLL_CHILD)
!define(CHROME_MULTIPLE_DLL_BROWSER)
,这两个条件基于是否定义
is_multi_dll_chrome
,而从
build/config/chrome_build.gni
可以看到只有Windows平台上,才会定义。所以前提条件是满足的。

运行
g_content_runner
执行的是
ContentMainRunnerImpl
的函数
Run()
,它会调用
RunNamedProcessTypeMain(…)
来根据参数
process_type
启动不同的进程,默认是空,启动的正式Browser进程。

在进程启动之前,会先调用
ShellMainDelegate::RunProcess(…)
,它会创建
BrowserMainRunner
,并作为参数进一步调用
shell_browser_main.cc
ShellBrowserMain(…)
,进而调用
BrowserMainRunner
Initialize(…)


在这个
Initialize(…)
函数中,又会创建
BrowserMainLoop
的实例,并对其依次进行如下调用:

Created with Raphaël 2.1.0Initialize(…)Init()EarlyInitialization()PreMainMessageLoopStart()PostMainMessageLoopStart()CreateStartupTasks()others

其中,
Init()
执行
parts_.reset(GetContentClient()->browser()->CreateBrowserMainParts(parameters_))
GetContentClient()
获得的就是前面提到的
g_client
,所以
GetContentClient()->browser()
得到的就是
browser_(ShellContentBrowserClient)
,调用其
CreateBrowserMainParts(…)
创建
ShellBrowserMainParts
实例。

其他的函数调用,其实都是调用
ShellBrowserMainParts
的函数,且都与GPU进程的启动相关,所以我们在后续文章继续分析。

回到
ContentMainRunnerImpl
的函数
RunNamedProcessTypeMain(...)
,最后会调用
browser_main.cc
BrowserMain()
,进而调用
BrowserMainRunnerImpl
Run()
等,启动Browser进程。

综上,我们可以画出UML图便于理解,如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息