chromium 84.0.4122.0 WebView apk 启动流程

  之前的博客介绍了chromium代码的下载和编译,调试环境的搭建。接下来我们根据编译的WebViewInstrumentation.apk来梳理浏览器的入口,看看chromium demo apk的启动流程。

  从日志上看WebViewInstrumentation.apk 的activity是:org.chromium.android_webview.shell/.AwShellActivity。我们看看webview apk的启动流程。

  当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。通常们是不需要指定一个Application的,系统会自动帮我们创建,WebViewInstrumentation apk在AndroidManifest.xml 文件中的application标签添加了AwShellApplication。所以我们先看看AwShellApplication.java都做了什么,代码如下:

public class AwShellApplication extends Application {
    // Called by the framework for ALL processes. Runs before ContentProviders are created.
    // Quirk: context.getApplicationContext() returns null during this method.
    @Override
    protected void attachBaseContext(Context context) {
        super.attachBaseContext(context);
        ContextUtils.initApplicationContext(this);
        PathUtils.setPrivateDataDirectorySuffix("webview", "WebView");
        CommandLine.initFromFile("/data/local/tmp/android-webview-command-line");
        ResourceBundle.setAvailablePakLocales(
                new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales());
    }
}

这部分的实现都是在base目录下,这里都是一些初始化的操作,这里也记住下commoand读取的路径。好了接着看AwShellActivity.java onCreate执行了哪些操作:

1 首先调用registerResources(this),其实现就是调用getResources获取resource,然后存储在AwResource全局变量sResources中,  设置Uuid存储在AwResource就没干啥了;

2  AwBrowserProcess.loadLibrary(null) ,加载库;

3 判断/data/local/tmp/android-webview-command-line是否含trace命令,如果有则设置trace enable;

4 createAwTestContainerView,可以看到其实现先调用AwBrowserProcess.start()起browser进程,new AwTestContainerView设置硬件渲染,new AwSettings部分参数设置,new AwDevToolsServer,将devtool调试开关设置为开;

5 然后是获取NavigationController,设置焦点、URL初始化,前进、后退安静初始化,加载URL

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AwShellResourceProvider.registerResources(this);

        AwBrowserProcess.loadLibrary(null);

        if (CommandLine.getInstance().hasSwitch(AwShellSwitches.ENABLE_ATRACE)) {
            Log.e(TAG, "Enabling Android trace.");
            TraceEvent.setATraceEnabled(true);
        }

        setContentView(R.layout.testshell_activity);

        mAwTestContainerView = createAwTestContainerView();

        mWebContents = mAwTestContainerView.getWebContents();
        mNavigationController = mWebContents.getNavigationController();
        LinearLayout contentContainer = (LinearLayout) findViewById(R.id.content_container);
        mAwTestContainerView.setLayoutParams(new LinearLayout.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1f));
        contentContainer.addView(mAwTestContainerView);
        mAwTestContainerView.requestFocus();

        initializeUrlField();
        initializeNavigationButtons();

        String startupUrl = getUrlFromIntent(getIntent());
        if (TextUtils.isEmpty(startupUrl)) {
            startupUrl = INITIAL_URL;
        }

        mAwTestContainerView.getAwContents().loadUrl(startupUrl);
        AwContents.setShouldDownloadFavicons();
        mUrlTextView.setText(startupUrl);
    }

代码路径:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java

接下来看看上面1~5的实现:

1、registerResources的实现,并没什么复杂的操作;

    public static void registerResources(Context context) {
        if (sInitialized) {
            return;
        }

        AwResource.setResources(context.getResources());

        AwResource.setConfigKeySystemUuidMapping(R.array.config_key_system_uuid_mapping);

        sInitialized = true;
    }

代码路径:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellResourceProvider.java

2、AwBrowserProcess.loadLibrary(null) ,加载库

    public static void loadLibrary(String processDataDirSuffix) {
        LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_WEBVIEW);
        if (processDataDirSuffix == null) {
            PathUtils.setPrivateDataDirectorySuffix(WEBVIEW_DIR_BASENAME, "WebView");
        } else {
            String processDataDirName = WEBVIEW_DIR_BASENAME + "_" + processDataDirSuffix;
            PathUtils.setPrivateDataDirectorySuffix(processDataDirName, processDataDirName);
        }
        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
        try {
            LibraryLoader.getInstance().loadNow();
            // Switch the command line implementation from Java to native.
            // It's okay for the WebView to do this before initialization because we have
            // setup the JNI bindings by this point.
            LibraryLoader.getInstance().switchCommandLineForWebView();
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }
    }

代码路径:./android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java

如果想跟整个so的加载流程,可以在loadWithSystemLinkerAlreadyLocked函数中添加堆栈,查看整个调用流程

    private void loadWithSystemLinkerAlreadyLocked(ApplicationInfo appInfo, boolean inZygote) {
        setEnvForNative();
        preloadAlreadyLocked(appInfo, inZygote);

        // If the libraries are located in the zip file, assert that the device API level is M or
        // higher. On devices <=M, the libraries should always be loaded by LegacyLinker.
        assert !isInZipFile() || Build.VERSION.SDK_INT >= VERSION_CODES.M;

        // Load libraries using the system linker.
        for (String library : NativeLibraries.LIBRARIES) {
            if (!isInZipFile()) {
                System.loadLibrary(library);
            } else {
                // Load directly from the APK.
                boolean is64Bit = ApiHelperForM.isProcess64Bit();
                String zipFilePath = appInfo.sourceDir;
                boolean crazyPrefix = forceSystemLinker(); // See comment in this function.
                String fullPath = zipFilePath + "!/"
                        + makeLibraryPathInZipFile(library, crazyPrefix, is64Bit);

                Log.i(TAG, "libraryName: %s", fullPath);
                System.load(fullPath);
            }
        }
    }

代码路径:./base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java

3 判断/data/local/tmp/android-webview-command-line是否含trace命令,如果有则设置trace enable

    public static void setATraceEnabled(boolean enabled) {
        if (sATraceEnabled == enabled) return;
        sATraceEnabled = enabled;
        if (enabled) {
            // Calls TraceEvent.setEnabled(true) via
            // TraceLog::EnabledStateObserver::OnTraceLogEnabled
            TraceEventJni.get().startATrace();
        } else {
            // Calls TraceEvent.setEnabled(false) via
            // TraceLog::EnabledStateObserver::OnTraceLogDisabled
            TraceEventJni.get().stopATrace();
        }
    }

代码路径:./base/android/java/src/org/chromium/base/TraceEvent.java

4 createAwTestContainerView的函数实现如下

    private AwTestContainerView createAwTestContainerView() {
        AwBrowserProcess.start();
        AwTestContainerView testContainerView = new AwTestContainerView(this, true);
 ......
        SharedPreferences sharedPreferences =
                getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
        if (mBrowserContext == null) {
            mBrowserContext = new AwBrowserContext(
                    sharedPreferences, AwBrowserContext.getDefault().getNativePointer(), true);
        }
        final AwSettings awSettings =
                new AwSettings(this /* context */, false /* isAccessFromFileURLsGrantedByDefault */,
                        false /* supportsLegacyQuirks */, false /* allowEmptyDocumentPersistence */,
                        true /* allowGeolocationOnInsecureOrigins */,
                        false /* doNotUpdateSelectionOnMutatingSelectionRange */);
        // Required for WebGL conformance tests.
        awSettings.setMediaPlaybackRequiresUserGesture(false);
        // Allow zoom and fit contents to screen
        awSettings.setBuiltInZoomControls(true);
        awSettings.setDisplayZoomControls(false);
        awSettings.setUseWideViewPort(true);
        awSettings.setLoadWithOverviewMode(true);
        awSettings.setLayoutAlgorithm(AwSettings.LAYOUT_ALGORITHM_TEXT_AUTOSIZING);

        testContainerView.initialize(new AwContents(mBrowserContext, testContainerView,
                testContainerView.getContext(), testContainerView.getInternalAccessDelegate(),
                testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings));
        testContainerView.getAwContents().getSettings().setJavaScriptEnabled(true);
        if (mDevToolsServer == null) {
            mDevToolsServer = new AwDevToolsServer();
            mDevToolsServer.setRemoteDebuggingEnabled(true);
        }
        return testContainerView;
    }

4.1 我们先来看如何browser线程,看start的实现如下,调用了startBrowserProcessesSync函数

    public static void start() {
        try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwBrowserProcess.start")) {
            final Context appContext = ContextUtils.getApplicationContext();
            AwDataDirLock.lock(appContext);
            // 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(() -> {
                boolean multiProcess =
                        CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
                if (multiProcess) {
                    ChildProcessLauncherHelper.warmUp(appContext, true);
                }
                // The policies are used by browser startup, so we need to register the policy
                // providers before starting the browser process. This only registers java objects
                // and doesn't need the native library.
                CombinedPolicyProvider.get().registerProvider(new AwPolicyProvider(appContext));

                // Check android settings but only when safebrowsing is enabled.
                try (ScopedSysTraceEvent e2 =
                                ScopedSysTraceEvent.scoped("AwBrowserProcess.maybeEnable")) {
                    AwSafeBrowsingConfigHelper.maybeEnableSafeBrowsingFromManifest(appContext);
                }

                try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
                             "AwBrowserProcess.startBrowserProcessesSync")) {
                    BrowserStartupController.getInstance().startBrowserProcessesSync(
                            LibraryProcessType.PROCESS_WEBVIEW, !multiProcess);
                }
            });
        }
    }

代码路径:./android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java

startBrowserProcessesSync 实现代码如下,通过调用contentStart函数,调用ContentMain.start()函数:

    public void startBrowserProcessesSync(
            @LibraryProcessType int libraryProcessType, boolean singleProcess) {
        ......
            if (contentStart() > 0) {
                // Failed. The callbacks may not have run, so run them.
                    enqueueCallbackExecution(STARTUP_FAILURE);
                    startedSuccessfully = false;
            }
        ......
    }

代码路径:./content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java

ContentMain.start函数实现如下,往下调用的函数是jni函数nativeStart:

    public static int start(boolean startServiceManagerOnly) {
        return ContentMainJni.get().start(startServiceManagerOnly);
    }

代码路径:./content/public/android/java/src/org/chromium/content/app/ContentMain.java

接下来我们看看nativeStart在c++做啥,函数实现如下,调用service_manager Main;

static jint JNI_ContentMain_Start(JNIEnv* env,
                                  jboolean start_service_manager_only) {
  TRACE_EVENT0("startup", "content::Start");

  DCHECK(!g_service_manager_main_delegate.Get() || !start_service_manager_only);

  if (!g_service_manager_main_delegate.Get()) {
    g_service_manager_main_delegate.Get() =
        std::make_unique(
            ContentMainParams(g_content_main_delegate.Get().get()));
  }

  static_cast(
      g_service_manager_main_delegate.Get().get())
      ->SetStartServiceManagerOnly(start_service_manager_only);

  service_manager::MainParams main_params(
      g_service_manager_main_delegate.Get().get());
  return service_manager::Main(main_params);
}

代码路径:./content/app/android/content_main.cc

接着往下看看service_manager Main做了什么,做了mojo的初始然,然后是通过delegate->RunEmbedderProcess()调用content_service_manager_main_delegate.cc的RunEmbedderProcess。

int Main(const MainParams& params) {
  MainDelegate* delegate = params.delegate;
  ......
  int exit_code = -1;
  base::debug::GlobalActivityTracker* tracker = nullptr;
  ProcessType process_type = delegate->OverrideProcessType();

  static bool is_initialized = false;

  if (!is_initialized) {
    is_initialized = true;
......
    mojo::core::Configuration mojo_config;
    if (process_type == ProcessType::kDefault &&
        command_line.GetSwitchValueASCII(switches::kProcessType) ==
            switches::kProcessTypeServiceManager) {
      mojo_config.is_broker_process = true;
    }
    mojo_config.max_message_num_bytes = kMaximumMojoMessageSize;
    delegate->OverrideMojoConfiguration(&mojo_config);
    mojo::core::Init(mojo_config);

    ui::RegisterPathProvider();

    tracker = base::debug::GlobalActivityTracker::Get();
    exit_code = delegate->Initialize(init_params);
    if (exit_code >= 0) {
      if (tracker) {
        tracker->SetProcessPhase(
            base::debug::GlobalActivityTracker::PROCESS_LAUNCH_FAILED);
        tracker->process_data().SetInt("exit-code", exit_code);
      }
      return exit_code;
    }

......

    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
            ::switches::kTraceToConsole)) {
      base::trace_event::TraceConfig trace_config =
          tracing::GetConfigForTraceToConsole();
      base::trace_event::TraceLog::GetInstance()->SetEnabled(
          trace_config, base::trace_event::TraceLog::RECORDING_MODE);
    }
  }

  const auto& command_line = *base::CommandLine::ForCurrentProcess();
  if (process_type == ProcessType::kDefault) {
    std::string type_switch =
        command_line.GetSwitchValueASCII(switches::kProcessType);
    if (type_switch == switches::kProcessTypeServiceManager) {
      process_type = ProcessType::kServiceManager;
    } else if (type_switch == switches::kProcessTypeService) {
      process_type = ProcessType::kService;
    } else {
      process_type = ProcessType::kEmbedder;
    }
  }
  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();
      exit_code = delegate->RunEmbedderProcess();
      break;
  }

  if (tracker) {
    if (exit_code == 0) {
      tracker->SetProcessPhaseIfEnabled(
          base::debug::GlobalActivityTracker::PROCESS_EXITED_CLEANLY);
    } else {
      tracker->SetProcessPhaseIfEnabled(
          base::debug::GlobalActivityTracker::PROCESS_EXITED_WITH_CODE);
      tracker->process_data().SetInt("exit-code", exit_code);
    }
  }
......

  if (process_type == ProcessType::kEmbedder)
    delegate->ShutDownEmbedderProcess();

  return exit_code;
}

代码路径:./services/service_manager/embedder/main.cc

然后可以看content_main_runner_impl.cc的Run函数,调用RegisterMainThreadFactories,创建InProcessUtility、InProcessRenderer、InProcessGpu线程并注册。然后是调用RunServiceManager,然后是创建起来的相关任务,我在browser_main_loop.cc的CreateStartupTasks函数中添加了堆栈,方便看流程。

int ContentMainRunnerImpl::Run(bool start_service_manager_only) {
......
  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.
  if (!process_type.empty() &&
      process_type != service_manager::switches::kZygoteProcess) {
    InitializeFieldTrialAndFeatureList();
    delegate_->PostFieldTrialInitialization();
  }

......
  RegisterMainThreadFactories();
  
  if (process_type.empty()){    
    return RunServiceManager(main_params, start_service_manager_only);
  }
  return RunOtherNamedProcessTypeMain(process_type, main_params, delegate_);
}

代码路径:./content/app/content_main_runner_impl.cc

堆栈打印的流程如下,接着往下可以看看都创建了什么任务:

./../../content/browser/browser_main_loop.cc:899			BrowserMainLoop::CreateStartupTasks
./../../content/browser/browser_main_runner_impl.cc:129		main_loop_->CreateStartupTasks();	
./../../android_webview/lib/aw_main_delegate.cc:307			int exit_code = browser_runner_->Initialize(main_function_params);	
./../../content/app/content_main_runner_impl.cc:502			int exit_code = delegate->RunProcess("", main_function_params);
./../../content/app/content_main_runner_impl.cc:950			return RunBrowserProcessMain(main_params, delegate_);
./../../content/app/content_main_runner_impl.cc:851			return RunServiceManager(main_params, start_service_manager_only);
./../../services/service_manager/embedder/main.cc:459		exit_code = delegate->RunEmbedderProcess();
./../../content/app/android/content_main.cc:62				return service_manager::Main(main_params);

4.2 然后是DevToolsServer对象的新建,devtool不太了解的小伙伴可以看看这个链接,devtool sever的开启便于后面的调试 https://developers.google.com/web/tools/chrome-devtools。这里只介绍如何启动devtool server,不介绍devtool server 如何实现。

        if (mDevToolsServer == null) {
            mDevToolsServer = new AwDevToolsServer();
            mDevToolsServer.setRemoteDebuggingEnabled(true);
        }
    public void setRemoteDebuggingEnabled(boolean enabled) {
        AwDevToolsServerJni.get().setRemoteDebuggingEnabled(
                AwDevToolsServer.this, mNativeDevToolsServer, enabled);
    }
static void JNI_AwDevToolsServer_SetRemoteDebuggingEnabled(
    JNIEnv* env,
    const JavaParamRef& obj,
    jlong server,
    jboolean enabled) {
  AwDevToolsServer* devtools_server =
      reinterpret_cast(server);
  if (enabled) {
    devtools_server->Start();
  } else {
    devtools_server->Stop();
  }
}

代码路径:./android_webview/browser/aw_devtools_server.cc

5 我们来看看URL的加载mAwTestContainerView.getAwContents().loadUrl(startupUrl);兜兜转转会调用到NavigationControllerImpl.java的loadUrl函数,调用一个JNI又是到C++了,调用的是navigation_controller_impl.cc的LoadURL,后面资源加载部分,再对整个请求流程进行分析。

    public void loadUrl(LoadUrlParams params) {
        if (mNativeNavigationControllerAndroid != 0) {
            NavigationControllerImplJni.get().loadUrl(mNativeNavigationControllerAndroid,
                    NavigationControllerImpl.this, params.getUrl(), params.getLoadUrlType(),
                    params.getTransitionType(),
                    params.getReferrer() != null ? params.getReferrer().getUrl() : null,
                    params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
                    params.getUserAgentOverrideOption(), params.getExtraHeadersString(),
                    params.getPostData(), params.getBaseUrl(), params.getVirtualUrlForDataUrl(),
                    params.getDataUrlAsString(), params.getCanLoadLocalResources(),
                    params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry());
        }
    }

代码路径:./content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java

往下就是开始资源加载了,在下篇博客中进行更新

你可能感兴趣的:(chromium)