編輯:關於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這個函數定義在文件external/chromium_org/content/app/android/content_main.cc中。> 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(); }
函數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中。
Android實現炫酷的CheckBox效果
首先貼出實現的效果圖:gif的效果可能有點過快,在真機上運行的效果會更好一些。我們主要的思路就是利用屬性動畫來動態地畫出選中狀態以及對勾的繪制過程。看到上面的效果圖,相信
Android Root原理初探
RootLinux:Root == Windows:AdminstratorAndroid是Linux系統嗎?操作系統 = 系統內核 + 文件系統Linux發行版:Lin
Android打造屬於自己的數據庫操作類。
1、概述開發Android的同學都知道sdk已經為我們提供了一個SQLiteOpenHelper類來創建和管理SQLite數據庫,通過寫一個子類去繼承它,就可以方便的創建
安卓Android-X86 4.4休眠、關機常見問題解決
Q:安卓Android-x86 4.4 VMware虛擬機休眠後怎麼喚醒?按一下右Alt鍵旁邊的菜單鍵就可以喚醒系統了。Q:安卓Android-x86 4.