先簡單介紹一下android裡面navive和js互相調用
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
類檔案路徑:
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版本