版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxc024000/article/details/81328525
WebView线程优先级设置
- WebView是基于Chromium开发的(在Chromium上封装了一层),用于展示Web页面的控件。其实,单从WebView这一层代码角度来看,它更像是Android为自身定制的Chromium内核接口层。Android通过WebView接口层,开启In-Process模式的chromium浏览器。
- WebView(Chromium)中有两个主要的概念:Browser和Render。随之对应的,Browser Thread与Render Thread。
- 一般来讲,一个比较复杂的系统或工程,不会设计成单进程/单线程模式(否则很容易崩溃、卡死)。
- 当系统以多进程/多线程模式设计,运行过程中会存在多进程/多线程间调度优先级的问题。这个时候,就需要为进程/线程设置优先级,以及调度算法等等方式,来解决该问题。
- 如上,接下来从代码角度,分析一下WebView是如何设置Browser Thread的优先级。
Browser Thread优先级设置
- Chromium版本:67.0.3369,下述所有代码均在对应的版本Chromium源码中。
- 在使用WebView时,实例化了WebView。通过调用函数ensureProviderCreated,保证成员mProvider被创建(WebView.java)
protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
// 看这里!!!
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
// Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
CookieSyncManager.setGetInstanceIsAllowed();
}
- ensureProviderCreated的实现如下,通过调用函数getFactory,得到WebViewFactoryProvider类型的对象,并调用该对象的方法getProvider,获得WebViewFactoryProvider类型的对象,然后使用该对象的函数createWebView,创建的真正的WebView交由mProvider管理。(WebView.java)
private void ensureProviderCreated() {
checkThread();
if (mProvider == null) {
// As this can get called during the base class constructor chain, pass the minimum
// number of dependencies here; the rest are deferred to init().
mProvider = getFactory().createWebView(this, new PrivateAccess());
}
}
private static synchronized WebViewFactoryProvider getFactory() {
return WebViewFactory.getProvider();
}
- 关于WebViewFactory.getProvider(),创建对象的实际类型,可以参考老罗的博客。或者看一下WebViewFactory.java中函数getProvider就了解了。
- 这里,直接说明函数,函数WebViewFactory.getProvider返回的实际对象的类型为WebViewChromiumFactoryProvider。
// WebView.java
// 因此,调用的实际上是,WebViewChromiumFactoryProvider.createWebView
mProvider = getFactory().createWebView(this, new PrivateAccess());
// WebbViewProver.java
// createWebView创建了WebViewChromium实例。
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
}
- 接下来,回到WebView.java文件中。接着,会调用mProiver.init,其实就是WebViewChromium的init函数。
protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
ensureProviderCreated();
// 看这里!!!
mProvider.init(javaScriptInterfaces, privateBrowsing);
// Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
CookieSyncManager.setGetInstanceIsAllowed();
}
- 通过调用WebViewChromium的init函数,会开启Browser线程。中间的具体过程省略,大致为
WebViewChromium(init) ->
WebViewChromiumFactoryProvider(startYourEngines) ->
WebViewChromiumAwInit(startYourEngines) ->
WebViewChromiumAwInit(ensureChromiumStartedLocked)->
WebViewChromiumAwInit(startChromiumLocked)->
AwBrowserProcess(start)->
BrowserStartupController(startBrowserProcessesSync) ->
ContentMain(start)
- ContentMain通过JNI方式,调用底层C++实现。
public class ContentMain {
/**
* Start the ContentMainRunner in native side.
**/
public static int start() {
// 看这里!!!
return nativeStart();
}
private static native int nativeStart();
}
- 对应C++实现(chromium/src/content/app/android/content_main.cc)
static jint JNI_ContentMain_Start(JNIEnv* env,
const JavaParamRef<jclass>& clazz) {
TRACE_EVENT0("startup", "content::Start");
DCHECK(!g_service_manager_main_delegate.Get());
g_service_manager_main_delegate.Get() =
std::make_unique<ContentServiceManagerMainDelegate>(
ContentMainParams(g_content_main_delegate.Get().get()));
service_manager::MainParams main_params(
g_service_manager_main_delegate.Get().get());
// 看这里!!!
return service_manager::Main(main_params);
}
- service_manager::Main方法,定义在(chromium/src/services/service_manager/embedder/main.cc)。
int Main(const MainParams& params) {
// 省略
switch (process_type) {
case ProcessType::kDefault:
NOTREACHED();
break;
case ProcessType::kServiceManager:
exit_code = RunServiceManager(delegate);
break;
case ProcessType::kService:
CommonSubprocessInit();
exit_code = RunService(delegate);
break;
case ProcessType::kEmbedder:
if (delegate->IsEmbedderSubprocess())
CommonSubprocessInit();
// 看这里!!!因为WebView是IN-process模式的Chromium,所以会走这个。
exit_code = delegate->RunEmbedderProcess();
break;
}
//省略
}
- RunEmbedderProcess函数的实现(chromium/src/content/app/content_service_manager_main_delegate.cc)
int ContentServiceManagerMainDelegate::RunEmbedderProcess() {
// 看这里!!!
return content_main_runner_->Run();
}
- 该run函数实现(externals/chromium/src/content/app/content_main_runner.cc)
int Run() override {
DCHECK(is_initialized_);
DCHECK(!is_shutdown_);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line.GetSwitchValueASCII(switches::kProcessType);
// Run this logic on all child processes. Zygotes will run this at a later
// point in time when the command line has been updated.
std::unique_ptr<base::FieldTrialList> field_trial_list;
if (!process_type.empty() && process_type != switches::kZygoteProcess)
InitializeFieldTrialAndFeatureList(&field_trial_list);
MainFunctionParams main_params(command_line);
main_params.ui_task = ui_task_;
main_params.created_main_parts_closure = created_main_parts_closure_;
#if defined(OS_WIN)
main_params.sandbox_info = &sandbox_info_;
#elif defined(OS_MACOSX)
main_params.autorelease_pool = autorelease_pool_;
#endif
// 看这里!!!
return RunNamedProcessTypeMain(process_type, main_params, delegate_);
}
// 看这里!!!RunNamedProcessTypeMain函数
int RunNamedProcessTypeMain(
const std::string& process_type,
const MainFunctionParams& main_function_params,
ContentMainDelegate* delegate) {
static const MainFunction kMainFunctions[] = {
#if !defined(CHROME_MULTIPLE_DLL_CHILD)
// 看这里!!!这个会把启动函数,注册进去。
{ "", BrowserMain },
#endif
#if !defined(CHROME_MULTIPLE_DLL_BROWSER)
#if BUILDFLAG(ENABLE_PLUGINS)
{ switches::kPpapiPluginProcess, PpapiPluginMain },
{ switches::kPpapiBrokerProcess, PpapiBrokerMain },
#endif // ENABLE_PLUGINS
{ switches::kUtilityProcess, UtilityMain },
{ switches::kRendererProcess, RendererMain },
{ switches::kGpuProcess, GpuMain },
#endif // !CHROME_MULTIPLE_DLL_BROWSER
};
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);
}
}
//省略
}
- 经过一系列调用过程,现在我们终于调用到了Browser线程(Webview模式下,会启动Browser以线程方式启动)的启动函数BrowserMain(chromium/src/content/browser/browser_main.cc)
int BrowserMain(const MainFunctionParams& parameters) {
ScopedBrowserMainEvent scoped_browser_main_event;
base::trace_event::TraceLog::GetInstance()->set_process_name("Browser");
base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
kTraceEventBrowserProcessSortIndex);
std::unique_ptr<BrowserMainRunner> main_runner(BrowserMainRunner::Create());
// 看这里!!!这里会对创建的Browser线程,做一些设置(Browser在启动时,会执行的一些任务)
int exit_code = main_runner->Initialize(parameters);
if (exit_code >= 0)
return exit_code;
// 看这里!!! 启动,并执行设置好的启动任务
exit_code = main_runner->Run();
main_runner->Shutdown();
return exit_code;
}
- main_runner为BrowserMainRunnerImpl类型,因此main_runner->Initialize(parameters)的实现如下(chromium/src/content/browser/browser_main_runner.cc)
int Initialize(const MainFunctionParams& parameters) override {
// 省略
const base::TimeTicks start_time_step2 = base::TimeTicks::Now();
// 看这里!!! 创建启动的Task,设置线程优先级的也在这里。
main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode();
if (result_code > 0)
return result_code;
UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep2Time",
base::TimeTicks::Now() - start_time_step2);
// Return -1 to indicate no early termination.
return -1;
}
- main_loop_->CreateStartupTasks()的具体实现,如下(chromium/src/content/browser/browser_main_loop.cc)
void BrowserMainLoop::CreateStartupTasks() {
TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks");
DCHECK(!startup_task_runner_);
#if defined(OS_ANDROID)
startup_task_runner_ = std::make_unique<StartupTaskRunner>(
base::Bind(&BrowserStartupComplete), base::ThreadTaskRunnerHandle::Get());
#else
startup_task_runner_ = std::make_unique<StartupTaskRunner>(
base::Callback<void(int)>(), base::ThreadTaskRunnerHandle::Get());
#endif
StartupTask pre_create_threads =
base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
startup_task_runner_->AddTask(pre_create_threads);
StartupTask create_threads =
base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
startup_task_runner_->AddTask(create_threads);
StartupTask post_create_threads =
base::Bind(&BrowserMainLoop::PostCreateThreads, base::Unretained(this));
startup_task_runner_->AddTask(post_create_threads);
// 看这里!!!添加了任务,在线程Run时,就会执行设置好的任务。
StartupTask browser_thread_started = base::Bind(
&BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
startup_task_runner_->AddTask(browser_thread_started);
StartupTask pre_main_message_loop_run = base::Bind(
&BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
startup_task_runner_->AddTask(pre_main_message_loop_run);
#if defined(OS_ANDROID)
if (parameters_.ui_task) {
// Running inside browser tests, which relies on synchronous start.
startup_task_runner_->RunAllTasksNow();
} else {
startup_task_runner_->StartRunningTasksAsync();
}
#else
startup_task_runner_->RunAllTasksNow();
#endif
}
- BrowserMainLoop::BrowserThreadsStarted的实现如下(chromium/src/content/browser/browser_main_loop.cc)
int BrowserMainLoop::BrowserThreadsStarted() {
TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted");
audio_service_runner_ =
base::MakeRefCounted<base::DeferredSequencedTaskRunner>();
// Bring up Mojo IPC and the embedded Service Manager as early as possible.
// Initializaing mojo requires the IO thread to have been initialized first,
// so this cannot happen any earlier than now.
InitializeMojo();
#if BUILDFLAG(ENABLE_MUS)
if (features::IsMusEnabled()) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableSurfaceSynchronization);
}
#endif
HistogramSynchronizer::GetInstance();
// 看这里!!!如果为Android平台,或定义了OS_CHROMEOS,就会将Browser Thread优先级设置为base::ThreadPriority::DISPLAY
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
// Up the priority of the UI thread.
base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY);
#endif
// 省略
}
- 如上,WebView最终将Browser Thread的优先级设置为base::ThreadPriority::DISPLAY,其具体定义如下(chromium/src/base/threading/platform_thread.h)。其实也很好理解,WebVIew/Chromium中,Browser负责将画面上屏,因此自然要设置其权限比Normal高一点。
enum class ThreadPriority : int {
// Suitable for threads that shouldn't disrupt high priority work.
BACKGROUND,
// Default priority level.
NORMAL,
// Suitable for threads which generate data for the display (at ~60Hz).
DISPLAY,
// Suitable for low-latency, glitch-resistant audio.
REALTIME_AUDIO,
};
- 额外:WebView中Render Thread默认的权限为base::ThreadPriority::NORMAL,有兴趣的可以看看Chromium源码是如何实现的(提示: chromium/src/content/browser/renderer_host/render_process_host_impl.cc 1526行)