WebView部分源码概览

Android与JS通过WebView互相调用方法(二者沟通的桥梁是WebView),实际上是:

Android去调用JS的代码
JS去调用Android的代码
对于Android调用JS代码的方法有2种:

通过 WebView.loadUrl()
通过 WebView.evaluateJavascript()
对于JS调用Android代码的方法有3种:

通过 WebView.addJavascriptInterface() 进行对象映射
通过 WebViewClient.shouldOverrideUrlLoading()方法回调拦截 url
通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
使用的示例可以参考:https://developer.android.google.cn/guide/webapps/webview?hl=zh_cn

跟踪WebView的loadUrl实现的时候,发现在AOSP的代码里面找不到,在网上也基本上找不到相关的说明,大部分是Android与JS互相调用的实例,以及chromium源代码下载、编译,原理实现等,感觉这中间还缺点什么,下方的目标是把这一块给串联起来

chromium源代码下载、编译
chromium源代码下载
代码下载推荐2种方式:

从gitee上下载 https://github.com/chromium/chromium
从github上下载 https://github.com/chromium
我是通过gitee直接下载zip包的,通过git clone命令下载,网速太感人了

当然有条件的,还是使用官方的提供的depot_tools,可以少踩一些坑

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:/path/to/depot_tools"
mkdir ~/chromium && cd ~/chromium
fetch --nohooks android
// 同步对应的平台的依赖编译
gclient sync
// 安装额外的编译依赖
build/install-build-deps-android.sh

chromium源代码编译
参考 https://github.com/chromium/chromium/blob/master/docs/android_build_instructions.md

// 设置编译平台
target_os = “android”
target_cpu = “arm64” # See “Figuring out target_cpu” below
// 启动编译
autoninja -C out/Default chrome_public_apk

不同的平台的编译方式不一样,具体的可以看
https://github.com/chromium/chromium/blob/master/docs/get_the_code.md

Webview
源代码路径:
frameworks/base/core/java/android/webkit/WebView.java

loadUrl和evaluateJavascript方法的实现比较简单,通过调用WebViewProvider来实现的

/**
 * Loads the given URL.
 * <p>
 * Also see compatibility note on {@link #evaluateJavascript}.
 *
 * @param url the URL of the resource to load
 */
public void loadUrl(String url) {
    checkThread();
    mProvider.loadUrl(url);
}

/**
 * Asynchronously evaluates JavaScript in the context of the currently displayed page.
 * If non-null, {@code resultCallback} will be invoked with any result returned from that
 * execution. This method must be called on the UI thread and the callback will
 * be made on the UI thread.
 * <p>
 * Compatibility note. Applications targeting {@link android.os.Build.VERSION_CODES#N} or
 * later, JavaScript state from an empty WebView is no longer persisted across navigations like
 * {@link #loadUrl(String)}. For example, global variables and functions defined before calling
 * {@link #loadUrl(String)} will not exist in the loaded page. Applications should use
 * {@link #addJavascriptInterface} instead to persist JavaScript objects across navigations.
 *
 * @param script the JavaScript to execute.
 * @param resultCallback A callback to be invoked when the script execution
 *                       completes with the result of the execution (if any).
 *                       May be {@code null} if no notification of the result is required.
 */
public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
    checkThread();
    mProvider.evaluateJavaScript(script, resultCallback);
}

WebViewProvider是一个接口,提供了WebView的能力,每个WebView绑定到一个具体的WebViewProvider对象上,这个对象实现了运行时WebView的行为能力

/**

  • WebView backend provider interface: this interface is the abstract backend to a WebView
  • instance; each WebView object is bound to exactly one WebViewProvider object which implements
  • the runtime behavior of that WebView.
  • All methods must behave as per their namesake in {@link WebView}, unless otherwise noted.
  • @hide Not part of the public API; only required by system implementors.
    */
    @SystemApi
    public interface WebViewProvider

WebViewProvider是一个接口,具体的实现不在framework里面。最开始的时候,WebView是android framework的一部分,从Android 5.0 (Lollipop)开始,WebView的实现由一个单独的apk来提供,apk 预置在设备里面,可以和普通应用一样更新

WebView的实现apk的源代码保存在
https://github.com/chromium/chromium/tree/master/android_webview
作为Chromium项目代码的一部分,当前AOSP已经不支持从源代码里面编译出WebView

在AOSP里面,针对不同平台预置了不同版本的WebView APK,适合的apk会默认包含在system image里面。apk文件可以在external/chromium-webview下找到

具体的说明可以参考
aosp-system-integration.md

chromium/android_webview
https://github.com/chromium/chromium/tree/master/android_webview

Android WebView是一个android系统组件,用来展示web内容,WebView以及相关的android类都是实现在chromium/android_webview目录下的

chromium/android_webview目录包含了android WebView的实现,也包含了AndroidX Webkit库的实现

其中WebViewProvider的实现类是WebViewChromium

class WebViewChromium implements WebViewProvider, WebViewProvider.ScrollDelegate,
WebViewProvider.ViewDelegate, SmartClipProvider
1
2
类文件路径:

android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java

其中loadUrl的实现为

@Override
public void loadUrl(final String url) {
    mFactory.startYourEngines(true);
    if (checkNeedsPost()) {
        // Disallowed in WebView API for apps targeting a new SDK
        assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
        mFactory.addTask(new Runnable() {
            @Override
            public void run() {
                mAwContents.loadUrl(url);
            }
        });
        return;
    }
    mAwContents.loadUrl(url);
}

在这段代码里面,有对android版本号做一个判断,从android 4.4开始,android上的WebView是基于Chromium内核来实现的。最终的实现是调用AwContents.loadUrl

在AwContents里面,通过逐层调用,最终调用此方法

/**
 * Load url without fixing up the url string. Consumers of ContentView are responsible for
 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
 * off during user input).
 *
 * @param params Parameters for this load.
 */
@VisibleForTesting
public void loadUrl(LoadUrlParams params) {
    if (params.getBaseUrl() == null) {
        // Don't record the URL if this was loaded via loadDataWithBaseURL(). That API is
        // tracked separately under Android.WebView.LoadDataWithBaseUrl.BaseUrl.
        recordLoadUrlScheme(schemeForUrl(params.getUrl()));
    }

    if (params.getLoadUrlType() == LoadURLType.DATA && !params.isBaseUrlDataScheme()) {
        // This allows data URLs with a non-data base URL access to file:///android_asset/ and
        // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also
        // allow access to file:// URLs (subject to OS level permission checks).
        params.setCanLoadLocalResources(true);
        AwContentsJni.get().grantFileSchemeAccesstoChildProcess(
                mNativeAwContents, AwContents.this);
    }

    // If we are reloading the same url, then set transition type as reload.
    if (params.getUrl() != null && params.getUrl().equals(mWebContents.getLastCommittedUrl())
            && params.getTransitionType() == PageTransition.TYPED) {
        params.setTransitionType(PageTransition.RELOAD);
    }
    params.setTransitionType(
            params.getTransitionType() | PageTransition.FROM_API);

    // For WebView, always use the user agent override, which is set
    // every time the user agent in AwSettings is modified.
    params.setOverrideUserAgent(UserAgentOverrideOption.TRUE);


    // We don't pass extra headers to the content layer, as WebViewClassic
    // was adding them in a very narrow set of conditions. See http://crbug.com/306873
    // However, if the embedder is attempting to inject a Referer header for their
    // loadUrl call, then we set that separately and remove it from the extra headers map/
    final String referer = "referer";
    Map<String, String> extraHeaders = params.getExtraHeaders();
    if (extraHeaders != null) {
        for (String header : extraHeaders.keySet()) {
            if (referer.equals(header.toLowerCase(Locale.US))) {
                params.setReferrer(
                        new Referrer(extraHeaders.remove(header), ReferrerPolicy.DEFAULT));
                params.setExtraHeaders(extraHeaders);
                break;
            }
        }
    }

    AwContentsJni.get().setExtraHeadersForUrl(mNativeAwContents, AwContents.this,
            params.getUrl(), params.getExtraHttpRequestHeadersString());
    params.setExtraHeaders(new HashMap<String, String>());

    // Ideally, the URL would only be "fixed" for user input (e.g. for URLs
    // entered into the Omnibox), but some WebView API consumers rely on
    // the legacy behavior where all navigations were subject to the
    // "fixing".  See also https://crbug.com/1145717.
    params.setUrl(UrlFormatter.fixupUrl(params.getUrl()).getPossiblyInvalidSpec());

    mNavigationController.loadUrl(params);

    // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit.
    // Chromium does not use this use code path and the best emulation of this behavior to call
    // request visited links once on the first URL load of the WebView.
    if (!mHasRequestedVisitedHistoryFromClient) {
        mHasRequestedVisitedHistoryFromClient = true;
        requestVisitedHistoryFromClient();
    }
}

TBD chromium还是很复杂,目前只有把WebView的启动以及怎么加载到Webview.apk里面的流程大概梳理出来,到具体的绘制的过程,还得好好研究研究

【一些参考资料】
WebView的启动过程
设计的一些关键概念

最后来一个chromium官方的视频
Android WebView 101 (Chrome University 2019)

WebView的层次结构

WebView的整体框架,android O版本及以上(renderer是一个单独的进程)

WebView的整体框架,android L到N版本

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值