WebView/Chromium线程优先级设置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxc024000/article/details/81328525

WebView线程优先级设置

  • WebView是基于Chromium开发的(在Chromium上封装了一层),用于展示Web页面的控件。其实,单从WebView这一层代码角度来看,它更像是Android为自身定制的Chromium内核接口层。Android通过WebView接口层,开启In-Process模式的chromium浏览器。
  • WebView(Chromium)中有两个主要的概念:BrowserRender。随之对应的,Browser ThreadRender 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_runnerBrowserMainRunnerImpl类型,因此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行)

猜你喜欢

转载自blog.csdn.net/zxc024000/article/details/81328525