1. 程式人生 > >Android WebView啟動Chromium渲染引擎的過程分析

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

       Android WebView載入了Chromium動態庫之後,就可以啟動Chromium渲染引擎了。Chromium渲染引擎由Browser、Render和GPU三端組成。其中,Browser端負責將網頁UI合成在螢幕上,Render端負責載入網頁的URL和渲染網頁的UI,GPU端負責執行Browser端和Render端請求的GPU命令。本文接下來詳細分析Chromium渲染引擎三端的啟動過程。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!

       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渲染引擎的過程

       從前面Android WebView載入Chromium動態庫的過程分析一文可以知道,當我們在App的UI中嵌入一個WebView時,WebView會在內部建立一個型別為WebViewChromium的Provider。Android WebView就是通過這個Provider來啟動和使用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。

       由於Chromium的GPU端實現在App的Render Thread中,這個Render Thread是由App負責啟動的,因此Chromium無需啟動它。不過,Chromium裡的android_webview模組會啟動一個DeferredGpuCommandService服務。當Chromium的Browser端和Render端需要執行GPU操作時,就會向DeferredGpuCommandService服務發出請求。這時候DeferredGpuCommandService服務又會通過App的UI執行緒將請求的GPU操作提交給App的Render Thread執行。這一點可以參考前面

Android WebView簡要介紹和學習計劃一文的描述。我們在接下來的一篇文章也會對Chromium的Browser端和Render端執行GPU操作的過程進行詳細的分析。

       接下來我們就結合原始碼,分析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端是否已經啟動。如果還沒有啟動,那麼就會進行啟動,如下所示:

public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
    ......

    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();
            return;
        }

        // 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.
            }
        }
    }

    ......
}
       這個函式定義在檔案frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java中。

       如果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端的,如下所示:

public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
    ......

    private void startChromiumLocked() {
        ......

        AwBrowserProcess.start(ActivityThread.currentApplication());

        ......
    }

    ......
}
       這個函式定義在檔案frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java中。

       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() {
            @Override
            public void run() {
                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的實現如下所示:

public class BrowserStartupController {
    ......

    void prepareToStartBrowserProcess(int maxRendererProcesses) throws ProcessInitException {
        ......

        nativeSetCommandLineFlags(maxRendererProcesses,
                nativeIsPluginEnabled() ? getPlugins() : null);
        ......
    }

    ......
}
       這個函式定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java中。

       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端,如下所示:

public class BrowserStartupController {
    ......

    int contentStart() {
        return ContentMain.start();
    }

    ......
}
       這個函式定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java中。

       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實現,如下所示:

__attribute__((visibility("default")))
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 =
    LAZY_INSTANCE_INITIALIZER;

......

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就會被呼叫,用來對它進行初始化。

       從前面Android WebView載入Chromium動態庫的過程分析一文可以知道,全域性變數g_content_main_delegate指向的是一個AwMainDelegate物件。這個AwMainDelegate物件將會封裝在一個ContentMainParams物件中,並且傳遞給前面建立的ContentMainRunnerImpl物件的成員函式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_);

    ......
}
       這個函式定義在檔案external/chromium_org/content/app/content_main_runner.cc中。

       引數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 {
 public:
  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();
}
       這個函式定義在檔案external/chromium_org/android_webview/lib/main/aw_main_delegate.cc中。

       AwMainDelegate類的成員函式CreateContentBrowserClient實際建立的是一個AwContentBrowserClient物件。這個AwContentBrowserClient物件是從ContentBrowserClient類繼承下來的。

       這意味著前面設定到Conent層的一個AwContentClient物件的成員變數browser_指向的是一個AwContentBrowserClient物件。這個AwContentBrowserClient物件在接下來啟動Chromium渲染引擎的Browser端過程中會使用到。

       這一步執行完成後,回到前面分析的函式Start中。這時候它就建立了一個ContentMainRunner物件,並且對這個ContentMainRunner物件進行初始化。接下來,函式Start繼續呼叫這個ContentMainRunner物件的成員函式Run,以便啟動Chromium渲染引擎的Browser端,如下所示:

class ContentMainRunnerImpl : public ContentMainRunner {
 public:
  ......

  virtual int Run() OVERRIDE {
    ......
    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    std::string process_type =
          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
  }

  ......
};
       這個函式定義在檔案external/chromium_org/content/app/content_main_runner.cc中。

       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 },
#endif
    ......
    { 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;
#endif
        if (exit_code >= 0)
          return exit_code;
      }
      return kMainFunctions[i].function(main_function_params);
    }
  }

  ......
}

       這個函式定義在檔案external/chromium_org/content/app/content_main_runner.cc中。

       函式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);
  ......
#else
  ......
#endif
}
      這個函式定義在檔案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 {
 public:
  ......

  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中。

       AwBrowserMainParts類的成員函式PreEarlyInitialization建立了一個MessageLoopForUI物件。這個MessageLoopForUI物件描述的就是一個Native層的UI Message Loop。從前面Chromium多執行緒模型設計和實現分析一文可以知道,Native層的UI Message Loop並沒有自己的執行緒,而是寄生在App的UI執行緒中執行(當前執行緒就是App的UI執行緒)。App的UI執行緒在Java層也有一個Message Loop,並且是由這個Java層的Message Loop驅動執行的。

       當我們往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();

  .....
}
       這個函式定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。

       BrowserMainLoop類的成員函式MainMessageLoopStart呼叫另外一個成員函式InitializeMainThread建立一個Browser Thread,如下所示:

void BrowserMainLoop::InitializeMainThread() {
  ......
  main_thread_.reset(
      new BrowserThreadImpl(BrowserThread::UI, base::MessageLoop::current()));
}
       這個函式定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。

       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);

    ......
  }

  ......
}
       這個函式定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。

       其中的一個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);
#endif
  return result_code_;
}
       這個函式定義在檔案external/chromium_org/content/browser/browser_main_loop.cc中。

       從前面的分析可以知道,函式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;

  ......
}
       這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

       從前面的呼叫過程可以知道,引數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物件的過程,如下所示:

class WebViewChromium implements WebViewProvider,
          WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
    ......

    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物件,如下所示:

public class AwContents {
    ......

    public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
            InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate,
            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實現,如下所示:

__attribute__((visibility("default")))
jlong
    Java_com_android_org_chromium_android_1webview_AwContents_nativeInit(JNIEnv*
    env, jclass jcaller,
    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中。

       函式Init是通過呼叫WebContents類的靜態成員函式Create建立一個WebContents物件的。WebContents類的靜態成員函式Create的實現,可以參考前面Chromium網頁Frame Tree建立過程分析一文。

       建立了一個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)),
      ...... {
  ......
}
       這個函式定義在檔案external/chromium_org/android_webview/native/aw_contents.cc中。

       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 {
 public:
  // 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物件,如下所示:

public class AwContents {
    ......

    private void setNewAwContents(long newAwContentsPtr) {
        ......

        mNativeAwContents = newAwContentsPtr;
        ......

        long nativeWebContents = nativeGetWebContents(mNativeAwContents);
        mContentViewCore = createAndInitializeContentViewCore(
                mContainerView, mContext, mInternalAccessAdapter, nativeWebContents,
                new AwGestureStateListener(), mContentViewClient, mZoomControls);
        .......
    }

    ......
}
       這個函式定義在檔案external/chromium_org/android_webview/java/src/org/chromium/android_webview/AwContents.java中。

       從前面的呼叫過程可以知道,引數newAwContentsPtr描述的是前面在Native層建立的AwContents物件。這個AwContents物件將會儲存在AwContents類的成員變數mNativeAwContents中。

        AwContents類的成員函式setNewAwContents接下來又會呼叫另外一個成員函式nativeGetWebContents獲得用來建立上述Native