http://mogoweb.net/archives/417
ContentShell是基于Content API的一个简单的浏览器外壳,下面我将分析chromium for android中的ContentShell程序的启动流程。
由于android程序的特殊性(一部分Java代码,一部分c++代码),所以看似简单的启动流程,在android版本的ContentShell却七拐八弯的,难以理清脉络。在android版本中,ContentShell采用了多进程模型,一个Browser进程加上若干Sandboxed子进程(也称作Render Process)。所以接下来我将以Browser进程和Sandboxed进程的创建为突破口,来弄清ContentShell的启动流程。
ContentShell的主Activity为ContentShellActivity,初始化和加载网页在其onCreate成员函数中实现。序列图如下:
1)创建ShellManager,ShellManager除了管理Shell外,本身也是一个FrameLayout。在native端进行初始化后,设置启动URL
2)调用ContentView的静态成员方法enableMultiProcess,最终调用到AndroidBrowserProcess的genericChromiumProcessInit()进行初始化,包括loadlibrary, loadresource,setcommandLine(设置render的数量), nativeInitApplicationContext()等
3)在AndroidBrowserProcess::genericChromiumProcessInit()中,会调用ContentMain的nativeStart(),在native端初始化一个全局的ContentMainRunner对象g_content_runner
4)ContentMainRunner::RunNamedProcessTypeMain() 中调用BrowserMain(), BrowserMain()中create,init并Run()一个BrowserMainRunner对象。
5)在Initialize过程中创建BrowserMainLoop并初始化一个BrowserThreadImpl做为Browser进程的主线程,开始message_loop,并通过BrowserThread::PostTask给主进程的消息队列发送任务。
至此Browser进程的创建和初始化完成.
在上图中,初始化过程并没有结束,下面就接着上图的start继续分析之后Shell的创建流程:
1)ContentMain类的start方法会调用native端的Start方法,在该方法中,会创建ContentMainRunnerImpl实例,并调用其Intialize方法进行初始化,初始化后调用Run方法。
2)Run方法中调用一个全局函数RunNamedProcessTypeMain,在该函数中,调用ShellMainDelegate的RunProcess方法,运行Browser进程。
3)RunProcess中会创建BrowserMainRunnerImpl实例,在进行初始化时,创建BrowserMainLoop对象,并执行其Init方法,在Init方法中,会创建一个非常重要的对象ShellBrowserMainParts
4)正是在ShellBrowserMainParts::PreMainMessageLoopRun中,调用Shell的静态成员方法CreateNewWindow,开启Shell创建过程的序幕。
Sandboxed进程的个数不定,与所选择的进程模型有关。chromium支持如下几种进程模型: Process-per-site-instance,Process-per-site,Process-per-tab,Single process。默认为Process-per-site-instance。
接着上图,在Shell的静态方法CreateNewWindow中,会进行一系列的初始化动作:
1)Shell::CreateNewWindow()用来创建新的Tab。此处会先create一个WebContents, WebContents负责Tab页面内网页内容的显示,跳转等。WebContents的render_manager_(RenderViewHostManager)成员负责管理RenderViewHost的创建和切换。Render_manager_初始化时会create新的SiteInstance和RenderViewHost, SiteInstance对应当前的url地址,而RenderViewHost则负责管理一个RenderView.
2)默认情况下,Chromium中的process是和SiteInstance一一对应的,即相同网站的tab使用相同的render进程处理,当tab在不同的网站切换时,也会同时切换render进程。在RenderViewHost构造时会获取SiteInstance对应的render进程,如果没有,就会创建一个新的render子进程RenderProcessHostImpl.
3)RenderProcessHost仍然是在Browser进程下的,它是RenderProcess子进程在Browser端的代表。 在RenderProcessHost的Init()函数中,通过ChildProcessLauncher工具类Launch()一个新的子进程,并最终调用到sandboxed_process_launcher.cc中的StartSandboxedProcess()函数
4)StartSandboxedProcess()将调用到java端的代码进行SandboxedProcessService进程的create, bind,此时第一个Sandboxed子进程SandboxedProcessService0将被创建,并调用nativeInitSandboxedProcess()对该进程进行初始化。
至此,ContentShell的初始化过程暂告一段落,此时Browser进程和Sandboxed进程将会创建出来。
其实在ContentShellActivity的onCreate方法调用之前,ContentShellApplication::onCreate代码已经执行了,这里进行了一些设置操作。另外,在加载native动态库so时,会调用JNI_OnLoad方法,在这里,除了注册native方法外,还执行了content::SetContentMainDelegate函数。
另一个需要注释的地方是:ContentShellActivity中的mShellManager.launchShell(shellUrl)语句并不会调用。开始分析代码的时候,以为是从这里开始加载流程,实际上由于ContentView.enableMultiProcess返回true,导致这段代码不会执行。