Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android WebView啟動Chromium渲染引擎的過程分析

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

編輯:關於Android編程

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

Android WebView使用了單進程架構的Chromium來加載和渲染網頁,因此它的Browser端、Render端和GPU端都不是以進程的形式存在的,而是以線程的形式存在。其中,Browser端實現在App的UI線程中,Render端實現在一個獨立的線程中,而GPU端實現在App的Render Thread中。注意,這是針對Android 5.0及以上版本的。Android在4.4版本引入基於Chromium實現的WebView,那時候GPU端與Browser一樣,都是實現在App的UI線程中。接下來我們只討論Android WebView在Android 5.0及以上版本的實現。

Android WebView啟動Chromium渲染引擎三端的過程如圖1所示:

\

圖1 Android WebView啟動Chromium渲染引擎的過程

從前面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 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(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(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 > g_content_runner =
    LAZY_INSTANCE_INITIALIZER;

LazyInstance > 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層的UIMessage Loop發送一個消息的時候,Native層的UIMessage Loop會向App的UI線程在Java層的Message Loop發送一個消息。當該消息被Java層的Message Loop調度執行的時候,之前發送在Native層的UIMessage Loop中的消息就會得到執行。Chromium渲染引擎的Browser端,就是以這種方式運行在App的UI線程中的。

AwBrowserMainParts類的成員函數PreEarlyInitialization在當前線程中創建了一個MessageLoopForUI對象之後,以後在當前線程中調用MessageLoopForUI類的靜態成員函數current時,就會獲得該MessageLoopForUI對象。有了這個MessageLoopForUI對象之後,AwBrowserMainParts類的成員函數PreEarlyInitialization就會調用它的成員函數Start,用來啟動它描述的NativeUI Message Loop。

這一步執行完成後,回到前面分析的BrowserMainRunnerImpl類的成員函數Initialize中,接下來BrowserMainLoop類的成員函數MainMessageLoopStart會被調用,用來創建一個Browser Thread,如下所示:

 

void BrowserMainLoop::MainMessageLoopStart() {
  ......

  InitializeMainThread();

  .....
}
這個函數定義在文件external/chromium_org/content/browser/browser_main_loop.cc中。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved