android webview加載了chromium動态庫之後,就可以啟動chromium渲染引擎了。chromium渲染引擎由browser、render和gpu三端組成。其中,browser端負責将網頁ui合成在螢幕上,render端負責加載網頁的url和渲染網頁的ui,gpu端負責執行browser端和render端請求的gpu指令。本文接下來詳細分析chromium渲染引擎三端的啟動過程。
android webview使用了單程序架構的chromium來加載和渲染網頁,是以它的browser端、render端和gpu端都不是以程序的形式存在的,而是以線程的形式存在。其中,browser端實作在app的ui線程中,render端實作在一個獨立的線程中,而gpu端實作在app的render thread中。注意,這是針對android 5.0及以上版本的。android在4.4版本引入基于chromium實作的webview,那時候gpu端與browser一樣,都是實作在app的ui線程中。接下來我們隻讨論android webview在android 5.0及以上版本的實作。
android webview啟動chromium渲染引擎三端的過程如圖1所示:
圖1 android webview啟動chromium渲染引擎的過程
chromium裡面有一個android_webview子產品。這個子產品提供了兩個類awbrowserprocess和awcontents,分别用來封裝chromium的content層提供的兩個接口類browserstartupcontroller和contentviewcore,它們分别用來啟動chromium的browser端和render端。
android webview啟動chromium的browser端,實際上就是在app的ui線程建立一個browser main loop。chromium以後需要請求browser端執行某一個操作時,就可以向這個browser main loop發送一個task。這個task最終會在app程序的ui線程中排程執行。
android webview啟動chromium的render端,實際上就是在目前的app程序中建立一個線程。以後網頁就由這個線程負責加載和渲染。這個線程稱為in-process renderer thread。
接下來我們就結合源碼,分析android webview啟動chromium的browser端和render端的過程。對于gpu端,我們僅僅分析與它相關的deferredgpucommandservice服務的啟動過程。在接下來一篇文章分析android webview執行gpu指令的過程時,我們再對gpu端進行更詳細的分析。
我們首先分析android webview啟動chromium的browser端的過程。前面提到,webview會在内部建立一個類型為webviewchromium的provider。有了這個provider之後,webview就可以調用它的成員函數init啟動chromium的browser端,如下所示:
class webviewchromium implements webviewprovider,
webviewprovider.scrolldelegate, webviewprovider.viewdelegate {
......
public void init(final map<string, object> javascriptinterfaces,
final boolean privatebrowsing) {
......
// 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) {
mfactory.startyourengines(false);
checkthread();
} else if (!mfactory.hasstarted()) {
if (looper.mylooper() == looper.getmainlooper()) {
mfactory.startyourengines(true);
}
}
mrunqueue.addtask(new runnable() {
@override
public void run() {
initforreal();
......
}
});
}
}
這個函數定義在檔案frameworks/webview/chromium/java/com/android/webview/chromium/webviewchromium.java中。
webviewchromium類的成員變量mfactory指向的是一個webviewchromiumfactoryprovider對象。webviewchromium類的成員函數init通過調用這個webviewchromiumfactoryprovider對象的成員函數startyourengines啟動chromium渲染引擎的browser端。
在android 4.3之前,webview隻能在app的ui線程中建立。相應地,webview也隻能在app的ui線程中啟動chromium渲染引擎的browser端。這時候webviewchromium類的成員函數init會傳遞一個參數true給webviewchromiumfactoryprovider類的成員函數startyourengines,表示如果目前線程如果不是ui線程,那麼就需要向ui線程發出一個通知,讓ui線程執行啟動chromium渲染引擎的browser端的操作。
在android 4.3及以後,webview也允許在app的非ui線程中建立。這時候webview允行在app的非ui線程中啟動chromium渲染引擎的browser端。是以,webviewchromium類的成員函數init就會傳遞一個參數false給webviewchromiumfactoryprovider類的成員函數startyourengines。
一般情況下,webview都是在app的ui線程中建立的。為了簡單起見,我們隻考慮這種情況。webviewchromium類的成員函數init調用webviewchromiumfactoryprovider類的成員函數startyourengines啟動了chromium渲染引擎的browser端之後,接下來還會向app的ui線程的消息隊列發送一個runnable。當該runnable被執行的時候,它就會調用webviewchromium類的成員函數initforreal建立圖1所示的awcontents對象。有了這個awcontents對象之後,後面就可以通過它來加載指定的url了。
接下來,我們首先分析webviewchromiumfactoryprovider類的成員函數startyourengines啟動chromium渲染引擎的browser端的過程,然後再分析webviewchromium類的成員函數initforreal為webview建立awcontents對象的過程。
webviewchromiumfactoryprovider類的成員函數startyourengines的實作如下所示:
public class webviewchromiumfactoryprovider implements webviewfactoryprovider {
void startyourengines(boolean onmainthread) {
synchronized (mlock) {
ensurechromiumstartedlocked(onmainthread);
這個函數定義在檔案frameworks/webview/chromium/java/com/android/webview/chromium/webviewchromiumfactoryprovider.java中。
webviewchromiumfactoryprovider類的成員函數startyourengines調用另外一個成員函數ensurechromiumstartedlocked檢查chromium渲染引擎的browser端是否已經啟動。如果還沒有啟動,那麼就會進行啟動,如下所示:
private void ensurechromiumstartedlocked(boolean onmainthread) {
if (mstarted) { // early-out for the common case.
return;
looper looper = !onmainthread ? looper.mylooper() : looper.getmainlooper();
threadutils.setuithread(looper);
if (threadutils.runningonuithread()) {
startchromiumlocked();
// 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.postonuithread(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.
如果chromium渲染引擎的browser端已經啟動,那麼webviewchromiumfactoryprovider類的成員變量mstarted的值就會等于true。在這種情況下,webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked什麼也不用做就可以傳回。
另一方面,如果chromium渲染引擎的browser端還沒有啟動,那麼webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked首先會根據參數onmainthread确定chromium渲染引擎的browser端要在哪個線程中運作。
當參數onmainthread的值等于true的時候,就表示chromium渲染引擎的browser端要在app的ui線程中運作。這時候如果目前線程不是app的ui線程,那麼webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked就會向app的ui線程的消息隊列發送一個runnable。當該runnable被執行的時候,才會啟動chromium渲染引擎的browser端。在這種情況下,目前線程也會等待app的ui線程啟動完成chromium渲染引擎的browser端。
當參數onmainthread的值等于true的時候,如果目前線程剛好也是app的ui線程,那麼webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked就可以馬上啟動chromium渲染引擎的browser端。
當參數onmainthread的值等于false的時候,不管目前線程是否app的ui線程,都表示chromium渲染引擎的browser端要在它裡面運作。是以,這時候webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked都會馬上啟動chromium渲染引擎的browser端。
無論是上述的哪一種情況,用來運作chromium渲染引擎的browser端的線程都會通過調用threadutils類的靜态成員函數setuithread記錄起來。以後webview都需要在該線程中通路chromium渲染引擎。
webviewchromiumfactoryprovider類的成員函數ensurechromiumstartedlocked是通過調用另外一個成員函數startchromiumlocked啟動chromium渲染引擎的browser端的,如下所示:
private void startchromiumlocked() {
awbrowserprocess.start(activitythread.currentapplication());
webviewchromiumfactoryprovider類的成員函數startchromiumlocked通過調用awbrowserprocess類的靜态成員函數start啟動chromium渲染引擎的browser端的,如下所示:
public abstract class awbrowserprocess {
public static void start(final context context) {
// 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(new runnable() {
try {
browserstartupcontroller.get(context).startbrowserprocessessync(
browserstartupcontroller.max_renderers_single_process);
} catch (processinitexception e) {
這個函數定義在檔案external/chromium_org/android_webview/java/src/org/chromium/android_webview/awbrowserprocess.java中。
前面提到,用來運作chromium渲染引擎的browser端的線程會通過threadutils類的靜态成員函數setuithread記錄起來。awbrowserprocess類的靜态成員函數start為了確定chromium渲染引擎的browser端在該線程中啟動,會通過調用threadutils類的靜态成員函數runonuithreadblocking檢查目前線程是否就是該線程。如果是的話,那麼就會直接啟動。否則的話,會向該線程的消息隊列發送一個runnable。當該runnable被執行的時候,再啟動chromium渲染引擎的browser端。
awbrowserprocess類的靜态成員函數start是通過調用目前app程序中的一個browserstartupcontroller單例對象的成員函數startbrowserprocessessync來啟動chromium渲染引擎的browser端的。這個browserstartupcontroller單例對象可以通過調用browserstartupcontroller類的靜态成員函數get獲得。
awbrowserprocess類的靜态成員函數start在啟動chromium渲染引擎的browser端的時候,會指定一個browserstartupcontroller.max_renderers_single_process參數。這個參數的值等于0,表示要啟動一個單程序架構的chromium渲染引擎。
接下來,我們就繼續分析chromium渲染引擎的browser端的啟動過程,也就是browserstartupcontroller類的成員函數startbrowserprocessessync的實作,如下所示:
public class browserstartupcontroller {
public void startbrowserprocessessync(int maxrenderers) throws processinitexception {
// if already started skip to checking the result
if (!mstartupdone) {
if (!mhasstartedinitializingbrowserprocess) {
preparetostartbrowserprocess(maxrenderers);
......
if (contentstart() > 0) {
// failed. the callbacks may not have run, so run them.
enqueuecallbackexecution(startup_failure, not_already_started);
這個函數定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/browserstartupcontroller.java中。
當browserstartupcontroller類的成員變量mstartupdone的值等于true的時候,就表示chromium渲染引擎的browser端已經啟動了。這時候browserstartupcontroller類的成員函數startbrowserprocessessync就什麼也不做就直接傳回。
另一方面,如果chromium渲染引擎的browser端還沒有啟動。這時候browserstartupcontroller類的成員函數startbrowserprocessessync就會調用另外一個成員函數contentstart進行啟動。
在啟動chromium渲染引擎的browser端之前,browserstartupcontroller類的成員函數startbrowserprocessessync也會檢查成員變量mhasstartedinitializingbrowserprocess的值。當這個值等于false的時候,就會先調用成員函數preparetostartbrowserprocess設定chromium渲染引擎的啟動參數。其中,最重要的就是将chromium渲染引擎設定為單程序架構。
接下來,我們先分析将chromium渲染引擎設定為單程序架構的過程,也就是browserstartupcontroller類的成員函數preparetostartbrowserprocess的實作,然後再分析啟動chromium渲染引擎的browser端的過程,也就是browserstartupcontroller類的成員函數contentstart的實作。
browserstartupcontroller類的成員函數preparetostartbrowserprocess的實作如下所示:
void preparetostartbrowserprocess(int maxrendererprocesses) throws processinitexception {
nativesetcommandlineflags(maxrendererprocesses,
nativeispluginenabled() ? getplugins() : null);
browserstartupcontroller類的成員函數preparetostartbrowserprocess調用另外一個成員函數nativesetcommandlineflags将chromium渲染引擎設定為單程序架構。
browserstartupcontroller類的成員函數nativesetcommandlineflags是一個jni方法,它由c++層的函數java_com_android_org_chromium_content_browser_browserstartupcontroller_nativesetcommandlineflags實作,如下所示:
__attribute__((visibility("default")))
void
java_com_android_org_chromium_content_browser_browserstartupcontroller_nativesetcommandlineflags(jnienv*
env, jclass jcaller,
jint maxrenderprocesses,
jstring plugindescriptor) {
return setcommandlineflags(env, jcaller, maxrenderprocesses,
plugindescriptor);
這個函數定義在檔案out/target/product/generic/obj/gyp/shared_intermediates/content/jni/browserstartupcontroller_jni.h中。
函數java_com_android_org_chromium_content_browser_browserstartupcontroller_nativesetcommandlineflags調用另外一個函數setcommandlineflags将chromium渲染引擎設定為單程序架構,如下所示:
static void setcommandlineflags(jnienv* env,
jclass clazz,
jint max_render_process_count,
jstring plugin_descriptor) {
std::string plugin_str =
(plugin_descriptor == null
? std::string()
: base::android::convertjavastringtoutf8(env, plugin_descriptor));
setcontentcommandlineflags(max_render_process_count, plugin_str);
這個函數定義在檔案external/chromium_org/content/browser/android/browser_startup_controller.cc中。
函數setcommandlineflags又會調用另外一個函數setcontentcommandlineflags将chromium渲染引擎設定為單程序架構,如下所示:
void setcontentcommandlineflags(int max_render_process_count,
const std::string& plugin_descriptor) {
......
commandline* parsed_command_line = commandline::forcurrentprocess();
int command_line_renderer_limit = -1;
if (parsed_command_line->hasswitch(switches::krendererprocesslimit)) {
std::string limit = parsed_command_line->getswitchvalueascii(
switches::krendererprocesslimit);
int value;
if (base::stringtoint(limit, &value)) {
command_line_renderer_limit = value;
if (value <= 0)
max_render_process_count = 0;
}
if (command_line_renderer_limit > 0) {
int limit = std::min(command_line_renderer_limit,
static_cast<int>(kmaxrendererprocesscount));
renderprocesshost::setmaxrendererprocesscount(limit);
} else if (max_render_process_count <= 0) {
// need to ensure the command line flag is consistent as a lot of chrome
// internal code checks this directly, but it wouldn't normally get set when
// we are implementing an embedded webview.
parsed_command_line->appendswitch(switches::ksingleprocess);
} else {
int default_maximum = renderprocesshost::getmaxrendererprocesscount();
dcheck(default_maximum <= static_cast<int>(kmaxrendererprocesscount));
if (max_render_process_count < default_maximum)
renderprocesshost::setmaxrendererprocesscount(max_render_process_count);
這個函數定義在檔案external/chromium_org/content/browser/android/content_startup_flags.cc中。
函數setcontentcommandlineflags首先檢查android webview是否設定了switches::krendererprocesslimit指令行參數。如果設定了,那麼這個參數的值就會被解析出來,儲存在本地變量command_line_renderer_limit中,用來限定chromium渲染引擎最多可建立的render程序的個數的。
chromium渲染引擎最多可建立的render程序的個數還受到參數max_render_process_count的限制:
1. 當本地變量command_line_renderer_limit的值大于0的時候,那麼取max_render_process_count和command_line_renderer_limit之間的較小者作為最多可建立的render程序的個數。
2. 當本地變量command_line_renderer_limit的值小于等于0,并且參數max_render_process_count的值也小于等于0的時候,那麼chromium渲染引擎不允許建立render程序,也就是它使用的是單程序架構。
3. 當本地變量command_line_renderer_limit的值小于等于0,并且參數max_render_process_count的值大于0的時候,會調用renderprocesshost類的靜态成員函數getmaxrendererprocesscount根據裝置記憶體的大小計算出可以建立的render程序的最大數default_maximum。如果參數max_render_process_count的值小于這個最大值,那麼就将它設定為可以建立的render程序的個數。
在我們這個情景中,android webview沒有設定switches::krendererprocesslimit指令行參數,并且參數max_render_process_count的值等于0,是以函數setcontentcommandlineflags會将chromium渲染引擎設定為單程序架構。這是通過在android webview的指令行參數中設定一個switches::ksingleprocess選項實作的。
這一步執行完成後,回到前面分析的browserstartupcontroller類的成員函數startbrowserprocessessync中,接下來它會調用另外一個成員函數contentstart啟動chromium渲染引擎的browser端,如下所示:
int contentstart() {
return contentmain.start();
browserstartupcontroller類的成員函數contentstart調用contentmain類的靜态成員函數start啟動chromium渲染引擎的browser端,如下所示:
public class contentmain {
public static int start() {
return nativestart();
這個函數定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/app/contentmain.java中。
contentmain類的靜态成員函數start調用另外一個靜态成員函數nativestart啟動chromium渲染引擎的browser端。
contentmain類的靜态成員函數nativestart是一個jni方法,它由c++層的函數java_com_android_org_chromium_content_app_contentmain_nativestart實作,如下所示:
jint java_com_android_org_chromium_content_app_contentmain_nativestart(jnienv*
env, jclass jcaller) {
return start(env, jcaller);
這個函數定義在檔案out/target/product/generic/obj/gyp/shared_intermediates/content/jni/contentmain_jni.h中。
函數java_com_android_org_chromium_content_app_contentmain_nativestart調用另外一個函數start啟動chromium渲染引擎的browser端,如下所示:
lazyinstance<scoped_ptr<contentmainrunner> > g_content_runner =
lazy_instance_initializer;
lazyinstance<scoped_ptr<contentmaindelegate> > g_content_main_delegate =
......
static jint start(jnienv* env, jclass clazz) {
if (!g_content_runner.get().get()) {
contentmainparams params(g_content_main_delegate.get().get());
g_content_runner.get().reset(contentmainrunner::create());
g_content_runner.get()->initialize(params);
return g_content_runner.get()->run();
這個函數定義在檔案external/chromium_org/content/app/android/content_main.cc中。
函數start判斷全局變量g_content_runner是否已經指向了一個contentmainrunner對象。如果還沒有指向,那麼就說明chromium渲染引擎的browser端還沒有啟動。在這種情況下,函數start就會調用contentmainrunner類的靜态成員函數create建立一個contentmainrunner對象,并且儲存在全局變量g_content_runner中。
contentmainrunner類的靜态成員函數create的實作如下所示:
contentmainrunner* contentmainrunner::create() {
return new contentmainrunnerimpl();
這個函數定義在檔案external/chromium_org/content/app/content_main_runner.cc中。
從這裡可以看到,contentmainrunner類的靜态成員函數create實際建立的是一個contentmainrunnerimpl對象。這個contentmainrunnerimpl傳回給函數start之後,它的成員函數initialize就會被調用,用來對它進行初始化。
contentmainrunnerimpl類的成員函數initialize的實作如下所示:
class contentmainrunnerimpl : public contentmainrunner {
public:
virtual int initialize(const contentmainparams& params) override {
delegate_ = params.delegate;
int exit_code;
if (delegate_ && delegate_->basicstartupcomplete(&exit_code))
return exit_code;
const commandline& command_line = *commandline::forcurrentprocess();
std::string process_type =
command_line.getswitchvalueascii(switches::kprocesstype);
contentclientinitializer::set(process_type, delegate_);
參數params描述的contentmainparams對象的成員變量delegate指向的是就是前面描述的全局變量g_content_main_delegate指向的awmaindelegate對象。contentmainrunnerimpl類的成員函數initialize會将這個awmaindelegate對象儲存在成員變量delegate_中,并且會調用這個awmaindelegate對象的成員函數basicstartupcomplete執行一些基本的初始化工作,如下所示:
bool awmaindelegate::basicstartupcomplete(int* exit_code) {
content::setcontentclient(&content_client_);
這個函數定義在檔案external/chromium_org/android_webview/lib/main/aw_main_delegate.cc中。
awmaindelegate類的成員變量content_client_描述的是一個awcontentclient對象。這個awcontentclient對象将會設定給chromium的content層。這個通過調用函數setcontentclient實作的,如下所示:
static contentclient* g_client;
void setcontentclient(contentclient* client) {
g_client = client;
contentclient* getcontentclient() {
return g_client;
這個函數定義在檔案external/chromium_org/content/public/common/content_client.cc中。
函數setcontentclient将參數client指向的一個awcontentclient對象儲存在全局變量g_client中。這個awcontentclient對象以後可以通過調用函數getcontentclient獲得。
這一步執行完成後,回到前面分析的contentmainrunnerimpl類的成員函數initialize的中,它接下來檢查android webview是否設定了switches::kprocesstype指令行參數。如果設定了,那麼該參數值process_type描述的就是目前啟動的程序的類型(browser端、render端或者gpu端)。如果沒有設定,那麼參數值process_type就會等于一個空字元串,表示目前要啟動的是一個browser端。
在我們這個情景中,android webview沒有設定switches::kprocesstype,是以得到的參數值process_type就等于一個空字元串。這個空字元串,連同contentmainrunnerimpl類的成員變量delegate_指向的awmaindelegate對象,會進一步傳遞給contentclientinitializer類的靜态成員函數set執行初始化操作,如下所示:
class contentclientinitializer {
static void set(const std::string& process_type,
contentmaindelegate* delegate) {
contentclient* content_client = getcontentclient();
if (process_type.empty()) {
if (delegate)
content_client->browser_ = delegate->createcontentbrowserclient();
......
};
這個函數定義在檔案external/chromium_org/content/app/content_main_runner.cc中。
contentclientinitializer類的靜态成員函數set首先是調用前面提到的函數getcontentclient獲得一個awcontentclient對象,接下來判斷參數process_type的值是否等于一個空字元串。如果等于的話,那麼就會調用參數delegate指向的一個awmaindelegate對象的成員函數createcontentbrowserclient建立一個contentbrowserclient對象,并且儲存在前面獲得的awcontentclient對象的成員變量browser_中。
從前面的分析可以知道,參數process_type的值等于一個空字元串,是以接下來contentclientinitializer類的靜态成員函數set就會調用參數delegate指向的awmaindelegate對象的成員函數createcontentbrowserclient建立一個contentbrowserclient對象,如下所示:
content::contentbrowserclient*
awmaindelegate::createcontentbrowserclient() {
content_browser_client_.reset(new awcontentbrowserclient(this));
return content_browser_client_.get();
awmaindelegate類的成員函數createcontentbrowserclient實際建立的是一個awcontentbrowserclient對象。這個awcontentbrowserclient對象是從contentbrowserclient類繼承下來的。
這意味着前面設定到conent層的一個awcontentclient對象的成員變量browser_指向的是一個awcontentbrowserclient對象。這個awcontentbrowserclient對象在接下來啟動chromium渲染引擎的browser端過程中會使用到。
這一步執行完成後,回到前面分析的函數start中。這時候它就建立了一個contentmainrunner對象,并且對這個contentmainrunner對象進行初始化。接下來,函數start繼續調用這個contentmainrunner對象的成員函數run,以便啟動chromium渲染引擎的browser端,如下所示:
virtual int run() override {
command_line.getswitchvalueascii(switches::kprocesstype);
mainfunctionparams main_params(command_line);
#if !defined(os_ios)
return runnamedprocesstypemain(process_type, main_params, delegate_);
#else
return 1;
#endif
contentmainrunner類的成員函數run首先獲得android webview設定的指令行參數switches::kprocesstype的值。前面提到,android webview沒有設定指令行參數switches::kprocesstype,是以這裡獲得的值為一個空字元串,也就是本地變量process_type的值等于一個空字元串。
接下來,contentmainrunner類的成員函數run還會将android webview設定的指令行參數封裝在一個mainfunctionparams對象中。這個mainfunctionparams對象,連同前面設定的本地變量process_type,以及contentmainrunner類的成員變量delegate_指向的一個awmaindelegate對象,會傳遞給另外一個函數runnamedprocesstypemain。這個函數将會負責啟動chromium渲染引擎的browser端,如下所示:
const mainfunctionparams& main_function_params,
contentmaindelegate* delegate) {
static const mainfunction kmainfunctions[] = {
#if !defined(chrome_multiple_dll_child)
{ "", browsermain },
{ switches::krendererprocess, renderermain },
{ switches::kgpuprocess, gpumain },
};
registermainthreadfactories();
for (size_t i = 0; i < arraysize(kmainfunctions); ++i) {
if (process_type == kmainfunctions[i].name) {
if (delegate) {
int exit_code = delegate->runprocess(process_type,
main_function_params);
#if defined(os_android)
// in android's browser process, the negative exit code doesn't mean the
// default behavior should be used as the ui message loop is managed by
// the java and the browser process's default behavior is always
// overridden.
if (process_type.empty())
return exit_code;
if (exit_code >= 0)
}
return kmainfunctions[i].function(main_function_params);
函數runnamedprocesstypemain定義了一個mainfunction數組。這個mainfunction數組用來指定不同類型的程序的入口函數。其中,browser程序、render程序和gpu程序對應的入口函數分别為browsermain、renderermain和gpumain。當然,隻有在參數delegate的值等于null的情況下,這個mainfunction數組才會生效。否則的話,所有程序的入口函數都為該參數指向的contentmaindelegate對象的成員函數runprocess。對于非browser程序,如果參數delegate指向的contentmaindelegate對象的成員函數runprocess的傳回值小于0,那麼上述mainfunction數組也會同樣生效。
從前面的調用過程可以知道,參數process_type的值是一個空字元串,表示函數runnamedprocesstypemain需要啟動的是一個chromium渲染引擎的browser程序(端)。這時候由于另外一個參數delegate指向了一個awmaindelegate對象,是以,函數runnamedprocesstypemain将調用這個awmaindelegate對象的成員函數runprocess啟動chromium渲染引擎的browser端。
函數runnamedprocesstypemain在調用參數delegate指向的awmaindelegate對象的成員函數runprocess啟動chromium渲染引擎的browser端之前,還會調用函數registermainthreadfactories注冊一些線程建立工廠函數,如下所示:
static void registermainthreadfactories() {
#if !defined(chrome_multiple_dll_browser)
renderprocesshostimpl::registerrenderermainthreadfactory(
createinprocessrendererthread);
這個函數定義在檔案external/chromium_org/content/app/content_main_runner.cc中。
其中的一個線程建立工廠函數是render線程建立工廠函數,它被指定為函數createinprocessrendererthread,并且會通過調用renderprocesshostimpl類的靜态成員函數registerrenderermainthreadfactory記錄起來,如下所示:
renderermainthreadfactoryfunction g_renderer_main_thread_factory = null;
void renderprocesshostimpl::registerrenderermainthreadfactory(
renderermainthreadfactoryfunction create) {
g_renderer_main_thread_factory = create;
這個函數定義在檔案external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。
參數create描述的函數createinprocessrendererthread将會儲存在全局變量g_renderer_main_thread_factory中。以後chromium渲染引擎的browser端将會通過這個函數建立in-process renderer thread,以便用來加載和渲染指定的url。
這一步執行完成後,回到前面分析的函數runnamedprocesstypemain,接下來它就會調用參數delegate指向的awmaindelegate對象的成員函數runprocess啟動chromium渲染引擎的browser端,如下所示:
int awmaindelegate::runprocess(
const std::string& process_type,
const content::mainfunctionparams& main_function_params) {
if (process_type.empty()) {
browser_runner_.reset(content::browsermainrunner::create());
int exit_code = browser_runner_->initialize(main_function_params);
return 0;
return -1;
這個函數定義在檔案external/chromium_org/android_webview/lib/main/aw_main_delegate.cc中。
從前面的調用過程可以知道,參數process_type的值等于一個空字元串。在這種情況下,awmaindelegate類的成員函數runprocess會調用browsermainrunner類的靜态成員函數create建立一個browsermainrunner對象,并且會儲存在成員變量browser_runner_中,如下所示:
browsermainrunner* browsermainrunner::create() {
return new browsermainrunnerimpl();
這個函數定義在檔案external/chromium_org/content/browser/browser_main_runner.cc中。
從這裡可以看到,browsermainrunner類的靜态成員函數create建立的實際上是一個browsermainrunnerimpl對象。這意味着awmaindelegate類的成員變量browser_runner_指向的是一個browsermainrunnerimpl對象。這個browsermainrunnerimpl對象的成員函數initialize接下來會被調用。在調用的過程中,就會将chromium渲染引擎的browser端啟動起來,如下所示:
class browsermainrunnerimpl : public browsermainrunner {
virtual int initialize(const mainfunctionparams& parameters) override {
if (!initialization_started_) {
initialization_started_ = true;
main_loop_.reset(new browsermainloop(parameters));
main_loop_->init();
main_loop_->earlyinitialization();
main_loop_->mainmessageloopstart();
main_loop_->createstartuptasks();
int result_code = main_loop_->getresultcode();
if (result_code > 0)
return result_code;
// return -1 to indicate no early termination.
return -1;
這個函數定義在檔案external/chromium_org/content/browser/browser_main_runner.cc中。
browsermainrunnerimpl類的成員函數initialize首先檢查成員變量initialization_started_的值是否等于true。如果等于true,那麼就說明chromium渲染引擎的browser端已經啟動過。在這種情況下,browsermainrunnerimpl類的成員函數initialize隻會建立一些startup task。
如果chromium渲染引擎的browser端還沒有啟動過,那麼browsermainrunnerimpl類的成員函數initialize首先就會建立一個browsermainloop對象,并且儲存在成員變量main_loop_中。接下來,browsermainrunnerimpl類的成員函數initialize會調用上述browsermainloop對象的成員函數init和earlyinitialization對其進行初始化。初始化完成後,它的成員函數mainmessageloopstart又會被調用。調用完成後,chromium渲染引擎的browser端就啟動完成了。啟動完成後,上述browsermainloop對象的成員函數createstartuptasks也會被調用,用來建立一些startup task。
接下來,我們就分别分析browsermainloop類的成員函數init、earlyinitialization、mainmessageloopstart和createstartuptasks的實作,以便了解chromium渲染引擎的browser端的啟動過程。
browsermainloop類的成員函數init用來建立一個browsermainparts對象,它的實作如下所示:
void browsermainloop::init() {
parts_.reset(
getcontentclient()->browser()->createbrowsermainparts(parameters_));
這個函數定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。
browsermainloop類的成員函數init首先調用前面提到的函數getcontentclient獲得一個awcontentclient對象,接下來又會調用這個awcontentclient對象的成員函數browser獲得它的成員變量browser_指向的一個awcontentbrowserclient對象。獲得了這個awcontentbrowserclient對象之後,就可以調用它的成員函數createbrowsermainparts建立一個browsermainparts對象,并且儲存在browsermainloop類的成員變量parts_中,如下所示:
content::browsermainparts* awcontentbrowserclient::createbrowsermainparts(
const content::mainfunctionparams& parameters) {
return new awbrowsermainparts(browser_context_.get());
這個函數定義在檔案external/chromium_org/android_webview/browser/aw_content_browser_client.cc中。
從這裡可以看出,awcontentbrowserclient類的成員函數createbrowsermainparts建立的實際上是一個awbrowsermainparts對象。這個awbrowsermainparts對象接下來會用來建立一個native層的ui message loop。這個ui message loop接下來又會用來建立一個browser thread,用來表示chromium渲染引擎的browser端。
這一步執行完成後,回到前面分析的browsermainrunnerimpl類的成員函數initialize中,接下來browsermainloop類的成員函數earlyinitialization會被調用,用來建立一個native層的ui message loop,如下所示:
void browsermainloop::earlyinitialization() {
if (parts_)
parts_->preearlyinitialization();
這個函數定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。
從前面的分析可以知道,browsermainloop類的成員變量parts_指向的是一個awbrowsermainparts對象。browsermainloop類的成員函數earlyinitialization會調用這個awbrowsermainparts對象的成員函數preearlyinitialization建立一個ui message loop,如下所示:
void awbrowsermainparts::preearlyinitialization() {
main_message_loop_.reset(new base::messageloopforui);
base::messageloopforui::current()->start();
這個函數定義在檔案external/chromium_org/android_webview/browser/aw_browser_main_parts.cc中。
當我們往native層的ui message loop發送一個消息的時候,native層的ui message loop會向app的ui線程在java層的message loop發送一個消息。當該消息被java層的message loop排程執行的時候,之前發送在native層的ui message loop中的消息就會得到執行。chromium渲染引擎的browser端,就是以這種方式運作在app的ui線程中的。
awbrowsermainparts類的成員函數preearlyinitialization在目前線程中建立了一個messageloopforui對象之後,以後在目前線程中調用messageloopforui類的靜态成員函數current時,就會獲得該messageloopforui對象。有了這個messageloopforui對象之後,awbrowsermainparts類的成員函數preearlyinitialization就會調用它的成員函數start,用來啟動它描述的native ui message loop。
這一步執行完成後,回到前面分析的browsermainrunnerimpl類的成員函數initialize中,接下來browsermainloop類的成員函數mainmessageloopstart會被調用,用來建立一個browser thread,如下所示:
void browsermainloop::mainmessageloopstart() {
initializemainthread();
.....
browsermainloop類的成員函數mainmessageloopstart調用另外一個成員函數initializemainthread建立一個browser thread,如下所示:
void browsermainloop::initializemainthread() {
main_thread_.reset(
new browserthreadimpl(browserthread::ui, base::messageloop::current()));
browsermainloop類的成員函數initializemainthread使用前面建立的native ui message loop建立了一個browser thread。這個browser thread描述的就是chromium渲染引擎的browser端。由于這個browser thread是使用前面建立的native ui message loop建立的,是以,它實際上描述的是app的ui線程。以後chromium請求這個browser thread執行操作時,這個操作就會在app的ui線程中執行。
這一步執行完成之後,chromium渲染引擎的browser端就啟動完成了。再回到前面分析的browsermainrunnerimpl類的成員函數initialize中,接下來browsermainloop類的成員函數createstartuptasks會被調用,用來在chromium渲染引擎的browser端執行一些startup task,如下所示:
void browsermainloop::createstartuptasks() {
if (!startup_task_runner_.get()) {
startuptask pre_create_threads =
base::bind(&browsermainloop::precreatethreads, base::unretained(this));
startup_task_runner_->addtask(pre_create_threads);
其中的一個startup task,是在chromium渲染引擎的browser端建立其它線程(io線程、資料庫線程、檔案線程等)之前執行的,對應的函數為browsermainloop類的成員函數precreatethreads。
browsermainloop類的成員函數precreatethread會檢查android webview的指令行參數是否設定了一個switches::ksingleprocess選項。如果設定了,那麼就會将chromium渲染引擎設定為單程序架構,如下所示:
int browsermainloop::precreatethreads() {
#if !defined(os_ios) && (!defined(google_chrome_build) || defined(os_android))
// single-process is an unsupported and not fully tested mode, so
// don't enable it for official chrome builds (except on android).
if (parsed_command_line_.hasswitch(switches::ksingleprocess))
renderprocesshost::setrunrendererinprocess(true);
return result_code_;
從前面的分析可以知道,函數setcontentcommandlineflags會給android webview的指令行參數設定一個switches::ksingleprocess選項。在這種情況下,browsermainloop類的成員函數precreatethread會調用renderprocesshost類的靜态成員函數setrunrendererinprocess将chromium渲染引擎設定為單程序架構,如下所示:
bool g_run_renderer_in_process_ = false;
void renderprocesshost::setrunrendererinprocess(bool value) {
g_run_renderer_in_process_ = value;
從前面的調用過程可以知道,參數value的值等于true。這時候renderprocesshost類的靜态成員函數setrunrendererinprocess就會将全局變量g_run_renderer_in_process_的值設定為true,表示chromium渲染引擎使用單程序加載,也就是在需要建立render程序來加載和渲染網頁時,通過一個in-process renderer thread模拟。
這一步執行完成後,chromium渲染引擎的browser端就啟動完畢。回到前面分析的webviewchromium類的成員函數init中,接下來它會繼續調用另外一個成員函數initforreal為webview建立一個awcontents對象。這個awcontents對象以後可以用來加載指定的url。
接下來,我們就繼續分析webviewchromium類的成員函數initforreal建立awcontents對象的過程,如下所示:
private void initforreal() {
mawcontents = new awcontents(mfactory.getbrowsercontext(), mwebview, ctx,
new internalaccessadapter(), new webviewnativegldelegate(),
mcontentsclientadapter, mwebsettings.getawsettings());
這個函數定義在檔案frameworks/webview/chromium/java/com/android/webview/chromium/webviewchromium.java中。
webviewchromium類的成員函數initforreal主要是建立了一個awcontents對象,并且儲存在成員變量mawcontents中。這個awcontents對象的建立過程,也就是awcontents類的構造函數的實作,如下所示:
public class awcontents {
public awcontents(awbrowsercontext browsercontext, viewgroup containerview, context context,
internalaccessdelegate internalaccessadapter, nativegldelegate nativegldelegate,
awcontentsclient contentsclient, awsettings awsettings) {
this(browsercontext, containerview, context, internalaccessadapter, nativegldelegate,
contentsclient, awsettings, new dependencyfactory());
這個函數定義在檔案external/chromium_org/android_webview/java/src/org/chromium/android_webview/awcontents.java中。
awcontents類的構造函數調用另外一個重載版本的構造函數建立一個awcontents對象,如下所示:
awcontentsclient contentsclient, awsettings settings,
dependencyfactory dependencyfactory) {
mnativegldelegate = nativegldelegate;
setnewawcontents(nativeinit(mbrowsercontext));
這個函數定義在檔案external/chromium_org/android_webview/java/src/org/chromium/android_webview/awcontents.java中。
參數nativegldelegate指向的是一個webviewnativegldelegate對象。這個webviewnativegldelegate對象會被儲存在awcontents類的成員變量mnativegldelegate中。
awcontents類的構造函數會調用另外一個成員函數nativeinit在native層建立一個webcontents對象。webcontents類是chromium的content層向外提供的一個類,通過它可以描述一個網頁。
在native層建立了一個webcontents對象之後,awcontents類的構造函數會将該webcontents對象傳遞給另外一個成員函數setnewawcontents,用來在native層建立一個contentviewcore對象。contentviewcore類同樣是chromium的content層向外提供的一個類,通過它可以加載一個指定的url,也就是通過可以啟動chromium渲染引擎的render端。
接下來,我們就繼續分析awcontents成員函數nativeinit和setnewawcontents的實作,以便了解android webview在native層contentviewcore對象的過程,為接下來分析chromium渲染引擎的render端的啟動過程做準備。
awcontents成員函數nativeinit是一個jni方法,它由c++層的函數java_com_android_org_chromium_android_1webview_awcontents_nativeinit實作,如下所示:
jlong
java_com_android_org_chromium_android_1webview_awcontents_nativeinit(jnienv*
jobject browsercontext) {
return init(env, jcaller, browsercontext);
這個函數定義在檔案out/target/product/generic/obj/gyp/shared_intermediates/android_webview/jni/awcontents_jni.h中。
函數java_com_android_org_chromium_android_1webview_awcontents_nativeinit調用另外一個函數init建立一個webcontents對象,并且使用這個webcontents對象建立一個native層的awcontents對象,如下所示:
static jlong init(jnienv* env, jclass, jobject browser_context) {
scoped_ptr<webcontents> web_contents(content::webcontents::create(
content::webcontents::createparams(awbrowsercontext::getdefault())));
return reinterpret_cast<intptr_t>(new awcontents(web_contents.pass()));
這個函數定義在檔案external/chromium_org/android_webview/native/aw_contents.cc中。
建立了一個webcontents對象之後,函數init就使用它來建立一個native層的awcontents對象,如下所示:
awcontents::awcontents(scoped_ptr<webcontents> web_contents)
: web_contents_(web_contents.pass()),
shared_renderer_state_(
browserthread::getmessageloopproxyforthread(browserthread::ui),
this),
browser_view_renderer_(
this,
&shared_renderer_state_,
web_contents_.get(),
browserthread::getmessageloopproxyforthread(browserthread::ui)),
...... {
awcontents類的構造函數首先将參數web_contents指向的webcontents對象儲存在成員變量web_contents_中,接下來又會分别構造一個sharedrendererstate對象和一個browserviewrenderer對象,并且儲存在成員變量shared_renderer_state_和browser_view_renderer_中。
通過awcontents類的成員變量shared_renderer_state_描述的sharedrendererstate對象,chromium渲染引擎的browser端和render端可以請求app的render thread執行gpu指令。同時,這個sharedrendererstate對象也會用來儲存chromium渲染引擎的render端渲染的每一幀資料。這些幀資料将會交給chromium渲染引擎的browser端合成顯示在螢幕上。
通過awcontents類的成員變量browser_view_renderer_描述的browserviewrenderer對象,則可以為chromium渲染引擎的render端建立一個synchronous compositor。這個synchronous compositor可以用來将網頁的cc layer tree渲染在一個synchronous compositor output surface上。
接下來我們就繼續分析上述sharedrendererstate對象和browserviewrenderer對象的構造過程。
sharedrendererstate對象的構造過程,也就是sharedrendererstate類的構造函數的實作,如下所示:
sharedrendererstate::sharedrendererstate(
scoped_refptr<base::messageloopproxy> ui_loop,
browserviewrendererclient* client)
: ui_loop_(ui_loop),
client_on_ui_(client),
這個函數定義在檔案external/chromium_org/android_webview$ vi browser/shared_renderer_state.cc中。
參數ui_loop描述的是一個native ui message loop。這個native ui message loop是通過前面調用browserthread類的靜态成員函數getmessageloopproxyforthread獲得的。這個native ui message loop會儲存在sharedrendererstate類的成員變量ui_loop_。以後通過這個成員變量,就可以向app的render thread請求執行gpu操作了。
另外一個參數client指向的就是前面建立的awcontents對象。這個awcontents對象會儲存在sharedrendererstate類的成員變量client_on_ui_中。
browserviewrenderer對象的構造過程,也就是browserviewrenderer類的構造函數的實作,如下所示:
browserviewrenderer::browserviewrenderer(
browserviewrendererclient* client,
sharedrendererstate* shared_renderer_state,
content::webcontents* web_contents,
const scoped_refptr<base::singlethreadtaskrunner>& ui_task_runner)
: client_(client),
content::synchronouscompositor::setclientforwebcontents(web_contents_, this);
這個函數定義在檔案external/chromium_org/android_webview/browser/browser_view_renderer.cc中。
從前面的調用過程可以知道,參數client指向的是前面建立的awcontents對象。這個awcontents對象會儲存在browserviewrenderer類的成員變量client_中。
browserviewrenderer類的構造函數接下來會調用synchronouscompositor類的靜态成員函數setclientforwebcontents建立一個synchronous compositor,如下所示:
void synchronouscompositor::setclientforwebcontents(
webcontents* contents,
synchronouscompositorclient* client) {
if (client) {
synchronouscompositorimpl::createforwebcontents(contents);
if (synchronouscompositorimpl* instance =
synchronouscompositorimpl::fromwebcontents(contents)) {
instance->setclient(client);
這個函數定義在檔案external/chromium_org/content/browser/android/in_process/synchronous_compositor_impl.cc中。
從前面的調用過程可以知道,參數client的值不等于null,它指向的是一個browserviewrenderer對象。在這種情況下,synchronouscompositor類的靜态成員函數setclientforwebcontents會調用synchronouscompositorimpl類的靜态成員函數createforwebcontents為前面建立的webcontents對象建立一個synchronous compositor。
synchronouscompositorimpl類的靜态成員函數createforwebcontents是從父類webcontentsuserdata<synchronouscompositorimpl>繼承下來的,它的實作如下所示:
template <typename t>
class webcontentsuserdata : public base::supportsuserdata::data {
// creates an object of type t, and attaches it to the specified webcontents.
// if an instance is already attached, does nothing.
static void createforwebcontents(webcontents* contents) {
if (!fromwebcontents(contents))
contents->setuserdata(userdatakey(), new t(contents));
這個函數定義在檔案external/chromium_org/content/public/browser/web_contents_user_data.h中。
webcontentsuserdata<synchronouscompositorimpl>類的靜态成員函數createforwebcontents首先調用另外一個fromwebcontents檢查之前是否已經為參數contents描述的webcontents對象建立過一個synchronouscompositorimpl對象。如果沒有建立過,那麼就會建立一個,并且儲存在該webcontents對象的内部,這是通過調用它的成員函數setuserdata實作的。這裡建立出來的synchronouscompositorimpl對象描述的就是一個ynchronous compositor。
這一步執行完成後,回到前面分析的synchronouscompositor類的靜态成員函數setclientforwebcontents中,接下來它又會通過synchronouscompositorimpl類的靜态成員函數fromwebcontents獲得前面建立的synchronouscompositorimpl對象,并且調用它的成員函數setclient,用來将參數client指向的browserviewrenderer對象儲存在内部,如下所示:
void synchronouscompositorimpl::setclient(
synchronouscompositorclient* compositor_client) {
dcheck(calledonvalidthread());
compositor_client_ = compositor_client;
這個函數定義在檔案external/chromium_org/content/browser/android/in_process/synchronous_compositor_impl.cc中。
synchronouscompositorimpl類的成員函數setclient将參數compositor_client指向的browserviewrenderer對象儲存在成員變量compositor_client_中。
這一步執行完成後,回到前面分析的java層的awcontents類的構造函數中,這時候就在native層建立一個webcontents對象、一個awcontents對象、一個sharedrendererstate對象、一個browserviewrenderer對象,以及一個synchronouscompositorimpl對象。這些對象後面在啟動chromium渲染引擎的render端,以及chromium渲染引擎的render端渲染網頁ui時,都會使用到。
java層的awcontents類的構造函數接下來會調用另外一個成員函數setnewawcontents在native層建立一個contentviewcore對象,如下所示:
private void setnewawcontents(long newawcontentsptr) {
mnativeawcontents = newawcontentsptr;
long nativewebcontents = nativegetwebcontents(mnativeawcontents);
mcontentviewcore = createandinitializecontentviewcore(
mcontainerview, mcontext, minternalaccessadapter, nativewebcontents,
new awgesturestatelistener(), mcontentviewclient, mzoomcontrols);
.......
從前面的調用過程可以知道,參數newawcontentsptr描述的是前面在native層建立的awcontents對象。這個awcontents對象将會儲存在awcontents類的成員變量mnativeawcontents中。
awcontents類的成員函數setnewawcontents接下來又會調用另外一個成員函數nativegetwebcontents獲得用來建立上述native層awcontents對象所使用的一個webcontents對象。有了這個webcontents對象之後,就使用它來在native層建立一個contentviewcore對象。這是通過調用awcontents類的成員函數createandinitializecontentviewcore實作的,如下所示:
private static contentviewcore createandinitializecontentviewcore(viewgroup containerview,
context context, internalaccessdelegate internaldispatcher, long nativewebcontents,
gesturestatelistener gesturestatelistener,
contentviewclient contentviewclient,
contentviewcore.zoomcontrolsdelegate zoomcontrolsdelegate) {
contentviewcore contentviewcore = new contentviewcore(context);
contentviewcore.initialize(containerview, internaldispatcher, nativewebcontents,
context instanceof activity ?
new activitywindowandroid((activity) context) :
new windowandroid(context.getapplicationcontext()));
return contentviewcore;
awcontents類的成員函數createandinitializecontentviewcore首先會建立一個java層的contentviewcore對象,然後再調用這個java層的contentviewcore對象的成員函數initialize對它進行初始化。在初始化的過程中,就會在native層建立一個對應的contentviewcore對象,如下所示:
public class contentviewcore
implements navigationclient, accessibilitystatechangelistener, screenorientationobserver {
public void initialize(viewgroup containerview, internalaccessdelegate internaldispatcher,
long nativewebcontents, windowandroid windowandroid) {
mnativecontentviewcore = nativeinit(
nativewebcontents, viewandroidnativepointer, windownativepointer,
mretainedjavascriptobjects);
這個函數定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/contentviewcore.java中。
contentviewcore類的成員函數initialize會調用另外一個成員函數nativeinit在native層建立一個contentviewcore對象,并且儲存在成員變量mnativecontentviewcore中。在建立這個native層的contentviewcore對象的時候,需要使用到參數nativewebcontents描述的一個native層的webcontents對象。
contentviewcore類的成員函數nativeinit是一個jni方法,它由c++層的函數java_com_android_org_chromium_content_browser_contentviewcore_nativeinit實作,如下所示:
java_com_android_org_chromium_content_browser_contentviewcore_nativeinit(jnienv*
env, jobject jcaller,
jlong webcontentsptr,
jlong viewandroidptr,
jlong windowandroidptr,
jobject retainedobjectset) {
return init(env, jcaller, webcontentsptr, viewandroidptr, windowandroidptr,
retainedobjectset);
這個函數定義在檔案out/target/product/generic/obj/gyp/shared_intermediates/content/jni/contentviewcore_jni.h中。
函數java_com_android_org_chromium_content_browser_contentviewcore_nativeinit調用另外一個函數init在native層建立一個contentviewcore對象,如下所示:
jlong init(jnienv* env,
jobject obj,
jlong native_web_contents,
jlong view_android,
jlong window_android,
jobject retained_objects_set) {
contentviewcoreimpl* view = new contentviewcoreimpl(
env, obj,
reinterpret_cast<webcontents*>(native_web_contents),
reinterpret_cast<ui::viewandroid*>(view_android),
reinterpret_cast<ui::windowandroid*>(window_android),
retained_objects_set);
return reinterpret_cast<intptr_t>(view);
這個函數定義在檔案external/chromium_org/content/browser/android/content_view_core_impl.cc中。
從這裡可以看到,函數init會使用參數native_web_contents描述的一個webcontents對象以及其它參數在native層建立一個contentviewcoreimpl對象。 contentviewcoreimpl類是從contentviewcore繼承下來的。
contentviewcoreimpl對象的建立過程,也就是contentviewcoreimpl類的構造函數的實作,如下所示:
contentviewcoreimpl::contentviewcoreimpl(
jnienv* env,
jobject obj,
webcontents* web_contents,
ui::viewandroid* view_android,
ui::windowandroid* window_android,
jobject java_bridge_retained_object_set)
: ......,
web_contents_(static_cast<webcontentsimpl*>(web_contents)),
contentviewcoreimpl類的構造函數會将參數web_contents指向的一個webcontents對象儲存在成員變量web_contents_中。以後通過contentviewcoreimpl類的成員函數loadurl加載指定的url時,就需要使用到這個webcontents對象。
在java層建立了一個awcontents對象和在native層建立了一個webcontents對象和一個contentviewcore對象之後,接下來我們就可以在android webview中加載指定的url了。android webview又會請求chromium渲染引擎啟動一個render端,并且在這個render端中加載指定的url。接下來,我們就從android webview中加載url開始,分析chromium渲染引擎啟動render端的過程。
android webview提供了一個成員函數loadurl,用來加載指定的url,如下所示:
public class webview extends absolutelayout
implements viewtreeobserver.onglobalfocuschangelistener,
viewgroup.onhierarchychangelistener, viewdebug.hierarchyhandler {
public void loadurl(string url) {
checkthread();
if (debugflags.trace_api) log.d(logtag, "loadurl=" + url);
mprovider.loadurl(url);
這個函數定義在檔案frameworks/base/core/java/android/webkit/webview.java中。
webview類的成員函數loadurl首先調用成員函數checkthread檢查目前線程是否是建立webview的線程。如果不是,那麼就會抛出一個異常出來。
@override
// early out to match old webview implementation
if (url == null) {
loadurl(url, null);
webviewchromium類的成員函數loadurl調用另外一個重載版本的成員函數loadurl加載參數url指定的url,如下所示:
public void loadurl(final string url, map<string, string> additionalhttpheaders) {
loadurlparams params = new loadurlparams(url);
if (additionalhttpheaders != null) params.setextraheaders(additionalhttpheaders);
loadurlonuithread(params);
webviewchromium類重載版本的成員函數loadurl将參數url和additinalhttpheaders封裝一個loadurlparams對象中,然後再傳這個loadurlparams對象傳遞給另外一個成員函數loadurlonuithread,如下所示:
private void loadurlonuithread(final loadurlparams loadurlparams) {
if (checkneedspost()) {
// disallowed in webview api for apps targetting a new sdk
assert mapptargetsdkversion < build.version_codes.jelly_bean_mr2;
mrunqueue.addtask(new runnable() {
mawcontents.loadurl(loadurlparams);
});
mawcontents.loadurl(loadurlparams);
webviewchromium類的成員函數loadurlparams會調用成員函數checkneedspost檢查目前線程是否就是webview的建立線程。如果不是,并且目前的android版本小于4.3,那麼就會向webview的建立線程的消息隊列發送一個runnable。當該runnable被執行的時候,才會調用webviewchromium類的成員變量mawcontents指向的一個awcontents對象的成員函數loadurl加載參數loadurlparam描述的url。
注意,如果目前線程不是webview的建立線程,并且目前的android版本大于等于4.3,那麼webviewchromium類的成員函數loadurlparams是不允許調用的。在我們這個情景中,前面已經保證了目前線程就是webview的建立線程。在這種情況下,webviewchromium類的成員函數loadurlparams就會直接調用成員變量mawcontents指向的一個awcontents對象的成員函數loadurl加載參數loadurlparam描述的url,如下所示:
public void loadurl(loadurlparams params) {
mcontentviewcore.loadurl(params);
從前面的分析可以知道,awcontents類的成員變量mcontentviewcore指向的是一個contentviewcore對象。awcontents類的成員函數loadurl調用這個contentviewcore對象的成員函數loadurl加載參數params描述的url。
bool renderprocesshostimpl::init() {
......
if (run_renderer_in_process()) {
......
in_process_renderer_.reset(g_renderer_main_thread_factory(channel_id));
base::thread::options options;
options.message_loop_type = base::messageloop::type_default;
in_process_renderer_->startwithoptions(options);
} else {
child_process_launcher_.reset(new childprocesslauncher(
new renderersandboxedprocesslauncherdelegate(channel_.get()),
cmd_line,
getid(),
this));
}
return true;
}
renderprocesshostimpl類的成員函數init調用另外一個成員函數run_renderer_in_process判斷要建立一個render程序還是一個render線程描述一個render端。
renderprocesshostimpl類的成員函數run_renderer_in_process是從父類renderprocesshost繼承下來的,它的實作如下所示:
bool renderprocesshost::run_renderer_in_process() {
return g_run_renderer_in_process_;
這個函數定義在檔案external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。
renderprocesshostimpl類的成員函數run_renderer_in_process傳回的是全局變量g_run_renderer_in_process_的值。從前面的分析可以知道,這個全局變量g_run_renderer_in_process_已經被android webview設定為true。是以,renderprocesshostimpl類的成員函數init會建立一個render線程來描述一個render端。
這個render線程是通過調用另外一個全局變量g_renderer_main_thread_factory描述的一個線程建立工廠函數建立的。從前面的分析可以知道,這個全局變量g_renderer_main_thread_factory描述的線程建立工廠函數為createinprocessrendererthread,它的實作如下所示:
base::thread* createinprocessrendererthread(const std::string& channel_id) {
return new inprocessrendererthread(channel_id);
這個函數定義在檔案external/chromium_org/content/renderer/in_process_renderer_thread.cc中。
從這裡可以看到,函數為createinprocessrendererthread建立的是一個inprocessrendererthread對象。這個inprocessrendererthread對象描述的是一個類型為in-process的render線程。這個render線程在renderprocesshostimpl類的成員函數init中将會被啟動起來。這時候android webview就将chromium渲染引擎的render端啟動起來了。
為了讓chromium渲染引擎可以使用app的render thread執行gpu指令,chromium的android_webview子產品會建立一個deferredgpucommandservice服務。這個deferredgpucommandservice服務将會負責請求app的render thread執行chromium渲染引擎的render端和browser端發出來的gpu指令。接下來我們就分析這個deferredgpucommandservice服務的建立過程。
deferredgpucommandservice服務是在android webview的成員函數ondraw被app的ui線程調用時建立的,是以接下來我們就從android webview的成員函數ondraw開始分析deferredgpucommandservice服務的建立過程,如下所示:
protected void ondraw(canvas canvas) {
mprovider.getviewdelegate().ondraw(canvas);
前面提到,webview類的成員變量mprovider指向的是一個webviewchromium對象。webview的成員函數ondraw調用這個webviewchromium對象的成員函數getviewdelegate獲得一個view delegate,如下所示:
// this needs to be kept thread safe!
public webviewprovider.viewdelegate getviewdelegate() {
return this;
從這裡可以看到,webviewchromium類的成員函數getviewdelegate傳回的view delegate就是目前正在處理的webviewchromium對象。這個webviewchromium對象傳回給webview類的成員函數ondraw之後,它的成員函數ondraw就會被調用,如下所示:
public void ondraw(final canvas canvas) {
runvoidtaskonuithreadblocking(new runnable() {
ondraw(canvas);
mawcontents.ondraw(canvas);
webviewchromium類的成員函數ondraw首先會調用成員函數checkneedspost檢查目前線程是否就是webview的建立線程。如果不是,那麼就會向webview的建立線程的消息隊列發送一個runnable。當該runnable被執行的時候,才會重新進入webviewchromium類的成員函數ondraw中,并且調用它的成員變量mawcontents指向的一個awcontents對象的成員函數ondraw,用來繪制網頁的ui。
如果目前線程就是webview的建立線程,那麼webviewchromium類的成員函數ondraw就會直接調用成員變量mawcontents指向的一個awcontents對象的成員函數ondraw繪制網頁的ui。
awcontents類的成員函數ondraw的實作如下所示:
public void ondraw(canvas canvas) {
mawviewmethods.ondraw(canvas);
awcontents類的成員變量mawviewmethods指向的是一個awviewmethodsimpl對象。awcontents類的成員函數ondraw調用這個awviewmethodsimpl對象的成員函數ondraw繪制網頁的ui,如下所示:
private long mnativeawcontents;
private class awviewmethodsimpl implements awviewmethods {
@override
public void ondraw(canvas canvas) {
if (!nativeondraw(mnativeawcontents, canvas, canvas.ishardwareaccelerated(),
mcontainerview.getscrollx(), mcontainerview.getscrolly(),
globalvisiblerect.left, globalvisiblerect.top,
globalvisiblerect.right, globalvisiblerect.bottom)) {
......
awviewmethodsimpl類的成員函數ondraw會調用外部類awcontents的成員函數nativeondraw繪制網頁的ui,并且會将外部類的成員變量mnativeawcontents描述的一個native層的awcontents對象傳遞給它。
awcontents類的成員函數nativeondraw是一個jni方法,它由c++層的函數java_com_android_org_chromium_android_1webview_awcontents_nativeondraw實作,如下所示:
jboolean
java_com_android_org_chromium_android_1webview_awcontents_nativeondraw(jnienv*
env,
jobject jcaller,
jlong nativeawcontents,
jobject canvas,
jboolean ishardwareaccelerated,
jint scrollx,
jint scrolly,
jint visibleleft,
jint visibletop,
jint visibleright,
jint visiblebottom) {
awcontents* native = reinterpret_cast<awcontents*>(nativeawcontents);
check_native_ptr(env, jcaller, native, "ondraw", false);
return native->ondraw(env, jcaller, canvas, ishardwareaccelerated, scrollx,
scrolly, visibleleft, visibletop, visibleright, visiblebottom);
函數java_com_android_org_chromium_android_1webview_awcontents_nativeondraw調用參數nativeawcontents描述的一個native層awcontents對象的成員函數ondraw繪制網頁的ui,如下所示:
bool awcontents::ondraw(jnienv* env,
jobject obj,
jobject canvas,
jboolean is_hardware_accelerated,
jint scroll_x,
jint scroll_y,
jint visible_left,
jint visible_top,
jint visible_right,
jint visible_bottom) {
dcheck(browserthread::currentlyon(browserthread::ui));
if (is_hardware_accelerated)
initializehardwaredrawifneeded();
return browser_view_renderer_.ondraw(
canvas,
is_hardware_accelerated,
gfx::vector2d(scroll_x, scroll_y),
gfx::rect(visible_left,
visible_top,
visible_right - visible_left,
visible_bottom - visible_top));
參數is_hardware_accelerated表示app的ui是否采用硬體加速方式繪制。如果是的話,那麼chromium也會使用硬體加速方式繪制網頁的ui。在這種情況下,awcontents類的成員函數ondraw首先會調用另外一個成員函數initializehardwaredrawifneeded檢查是否需要為chromium初始化一個硬體加速渲染環境。這個硬體加速渲染環境初始化完成後,awcontents類的成員函數ondraw才會調用成員變量browser_view_renderer_描述的一個browserviewrenderer對象的成員函數ondraw繪制網頁的ui。
awcontents類的成員函數initializehardwaredrawifneeded在為chromium初始化硬體加速渲染環境的過程中,就會建立一個deferredgpucommandservice服務,如下所示:
void awcontents::initializehardwaredrawifneeded() {
glviewrenderermanager* manager = glviewrenderermanager::getinstance();
base::autolock lock(render_thread_lock_);
if (renderer_manager_key_ == manager->nullkey()) {
renderer_manager_key_ = manager->pushback(&shared_renderer_state_);
deferredgpucommandservice::setinstance();
在chromium渲染引擎中,存在一個glviewrenderermanager單例對象。這個glviewrenderermanager單例對象可以通過調用glviewrenderermanager類的靜态成員函數getinstance獲得,它用來記錄目前有哪些webview是采用硬體加速方式繪制的。
awcontents類的成員函數initializehardwaredrawifneeded會檢查成員變量renderer_manager_key_的值是否等于一個null key。如果等于的話,那麼就說明目前正在繪制的webview還沒有初始化過硬體加速渲染環境。這時候awcontents類的成員函數initializehardwaredrawifneeded就會将成員變量shared_renderer_state_描述的一個sharedrendererstate對象添加到上述glviewrenderermanager單例對象中。添加之後,會獲得一個key。這個key就儲存在awcontents類的成員變量renderer_manager_key_。同時,deferredgpucommandservice類的靜态成員函數setinstance會被調用,用來建立一個deferredgpucommandservice服務。這時候就表示目前正在繪制的webview的硬體加速渲染環境初始化好了。
接下來我們繼續分析deferredgpucommandservice類的靜态成員函數setinstance會建立ferredgpucommandservice服務的過程,如下所示:
base::lazyinstance<scoped_refptr<deferredgpucommandservice> >
g_service = lazy_instance_initializer;
void deferredgpucommandservice::setinstance() {
if (!g_service.get()) {
g_service.get() = new deferredgpucommandservice;
content::synchronouscompositor::setgpuservice(g_service.get());
deferredgpucommandservice* deferredgpucommandservice::getinstance() {
dcheck(g_service.get().get());
return g_service.get().get();
這個函數定義在檔案external/chromium_org/android_webview/browser/deferred_gpu_command_service.cc中。
deferredgpucommandservice類的靜态成員函數setinstance檢查全局變量g_service是否已經指向了一個deferredgpucommandservice對象。如果還沒有指向,那麼就會建立一個deferredgpucommandservice對象讓它指向。建立出來的deferredgpucommandservice對象描述的就是一個deferredgpucommandservice服務。這個deferredgpucommandservice服務可以通過調用deferredgpucommandservice類的靜态成員函數getinstance獲得。
deferredgpucommandservice類的靜态成員函數setinstance建立出來的deferredgpucommandservice服務會被設定為android webview繪制網頁所使用的synchronous compositor的gpu服務,這是通過調用synchronouscompositor類的靜态成員函數setgpuservice實作的,如下所示:
base::lazyinstance<synchronouscompositorfactoryimpl>::leaky g_factory =
void synchronouscompositor::setgpuservice(
scoped_refptr<gpu::inprocesscommandbuffer::service> service) {
g_factory.get().setdeferredgpuservice(service);
全局變量g_factory描述的是一個synchronouscompositorfactoryimpl對象,synchronouscompositor類的靜态成員函數setgpuservice會将參數service描述的一個deferredgpucommandservice服務儲存在它内部。這個通過調用它的成員函數setdeferredgpuservice實作的,如下所示
void synchronouscompositorfactoryimpl::setdeferredgpuservice(
dcheck(!service_);
service_ = service;
這個函數定義在檔案external/chromium_org/content/browser/android/in_process/synchronous_compositor_factory_impl.cc中。
synchronouscompositorfactoryimpl類的成員函數setdeferredgpuservice将參數service描述的一個deferredgpucommandservice服務儲存在成員變量service_。
這一步執行完成後,chromium渲染引擎的render端以後就會通過儲存在synchronouscompositorfactoryimpl類的成員變量service_中的deferredgpucommandservice服務來執行gpu指令。這一點我們在接下來一篇文章中再詳細分析。
至此,我們就分析完成了android webview啟動chromium渲染引擎的過程,主要就是啟動的chromium渲染引擎的browser端和render端。其中,browser端對應的就是app的ui線程,而render端對應的是一個類型為in-process的render線程。
由于android webview要求chromium渲染引擎使用app的render thread執行gpu指令,是以chromium渲染引擎就不會建立自己的gpu端。不過,它會建立一個deferredgpucommandservice服務,用來将chromium渲染引擎發出的gpu指令交給app的render thread執行。在接下來一篇文章中,我們就詳細分析chromium渲染引擎執行gpu指令的過程。