天天看點

Android WebView啟動Chromium渲染引擎的過程分析

  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所示:

Android WebView啟動Chromium渲染引擎的過程分析

圖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指令的過程。