瀏覽器從界面上看都很簡單,輸入一個地址後就可以跳轉,今天這裏先整理chromium AndroidWebView.apk中從輸入URL,到資源加載的流程,暫不涉及渲染,渲染後續進行介紹。從Java層往c++層逐步分析。
AndroidWebView.apk啓動後有個地址輸入欄,如果輸入地址按下確定鍵會調用到loadUrl進行url加載。
private void initializeUrlField() {
mUrlTextView = (EditText) findViewById(R.id.url);
mUrlTextView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((actionId != EditorInfo.IME_ACTION_GO) && (event == null
|| event.getKeyCode() != KeyEvent.KEYCODE_ENTER
|| event.getAction() != KeyEvent.ACTION_DOWN)) {
return false;
}
......
mAwTestContainerView.getAwContents().loadUrl(url);
mUrlTextView.clearFocus();
setKeyboardVisibilityForUrl(false);
mAwTestContainerView.requestFocus();
return true;
}
});
......
}
代碼路徑:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java。
NavigationController.java 只定義了loadUrl接口,實現是在NavigationControllerImpl.java中,其實現是調用jni接口。
public void loadUrl(LoadUrlParams params) {
//Log.e("NavigationControllerImpl.java", "loadUrl Url()"+params.getUrl());
//Exception e = new Exception("Chrome_debug");
//e.printStackTrace();
if (mNativeNavigationControllerAndroid != 0) {
nativeLoadUrl(mNativeNavigationControllerAndroid, 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
nativeLoadUrl 調用到的是navigation_controller_android.cc LoadURL函數
void NavigationControllerAndroid::LoadUrl(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& url,
jint load_url_type,
jint transition_type,
const JavaParamRef<jstring>& j_referrer_url,
jint referrer_policy,
jint ua_override_option,
const JavaParamRef<jstring>& extra_headers,
const JavaParamRef<jobject>& j_post_data,
const JavaParamRef<jstring>& base_url_for_data_url,
const JavaParamRef<jstring>& virtual_url_for_data_url,
const JavaParamRef<jstring>& data_url_as_string,
jboolean can_load_local_resources,
jboolean is_renderer_initiated,
jboolean should_replace_current_entry) {
......
navigation_controller_->LoadURLWithParams(params);
}
代碼路徑:./content/browser/frame_host/navigation_controller_android.cc
LoadURLWithParams調用LoadEntry
void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
......
LoadEntry(std::move(entry));
}
LoadEntry接着往下的調用關係如下圖,如果有興趣,可以在render_frame_host_impl.cc Navigate函數中添加堆棧打印,也可以打印出想過堆棧,進而獲取調用關係:
接着看看Navigate函數的實現,這裏需要關注的是navigate調用了SendNavigateMessage:
void RenderFrameHostImpl::Navigate(
const CommonNavigationParams& common_params,
const StartNavigationParams& start_params,
const RequestNavigationParams& request_params) {
......
if (navigations_suspended_) {
// This may replace an existing set of params, if this is a pending RFH that
// is navigated twice consecutively.
suspended_nav_params_.reset(
new NavigationParams(common_params, start_params, request_params));
} else {
// Get back to a clean state, in case we start a new navigation without
// completing an unload handler.
ResetWaitingState();
//YH_LOGD("<render_frame_host_impl.cc>[%s][%d]\n",__FUNCTION__,__LINE__);
SendNavigateMessage(common_params, start_params, request_params);
}
......
}
SendNavigateMessage 的實現如下,是發送一個FrameMsg_Navigate msg,是browser線程發送,render線程接收,資源的加載在render線程中進行實現,可以在render線程中添加堆棧,跟蹤資源加載流程:
void RenderFrameHostImpl::SendNavigateMessage(
const CommonNavigationParams& common_params,
const StartNavigationParams& start_params,
const RequestNavigationParams& request_params) {
......
RenderFrameDevToolsAgentHost::OnBeforeNavigation(
frame_tree_node_->current_frame_host(), this);
Send(new FrameMsg_Navigate(
routing_id_, common_params, start_params, request_params));
}
看看render_frame_impl.cc文件中監聽了FrameMsg_Navigate消息,收到FrameMsg_Navigate消息執行OnNavigate函數
bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
......
IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)
......
IPC_END_MESSAGE_MAP()
return handled;
}
OnNavigate接着往下的調用關係如下圖,
來看看StartAsync的實現,又是發送一個ResourceHostMsg_RequestResource給browser線程進行處理:
int ResourceDispatcher::StartAsync(
std::unique_ptr<ResourceRequest> request,
int routing_id,
scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,
const GURL& frame_origin,
std::unique_ptr<RequestPeer> peer,
blink::WebURLRequest::LoadingIPCType ipc_type,
mojom::URLLoaderFactory* url_loader_factory) {
......
if (ipc_type == blink::WebURLRequest::LoadingIPCType::Mojo) {
std::unique_ptr<URLLoaderClientImpl> client(
new URLLoaderClientImpl(request_id, this, main_thread_task_runner_));
mojom::URLLoaderPtr url_loader;
url_loader_factory->CreateLoaderAndStart(
GetProxy(&url_loader), routing_id, request_id, *request,
client->CreateInterfacePtrAndBind());
pending_requests_[request_id]->url_loader = std::move(url_loader);
pending_requests_[request_id]->url_loader_client = std::move(client);
} else {
//base::debug::StackTrace();
message_sender_->Send(
new ResourceHostMsg_RequestResource(routing_id, request_id, *request));
}
return request_id;
}
resource_dispatcher_host_impl.cc監聽ResourceHostMsg_RequestResource,調用OnRequestResource,
void ResourceDispatcherHostImpl::OnRequestResource(
int routing_id,
int request_id,
const ResourceRequest& request_data) {
OnRequestResourceInternal(routing_id, request_id, request_data, nullptr,
nullptr);
}
void ResourceDispatcherHostImpl::OnRequestResourceInternal(
int routing_id,
int request_id,
const ResourceRequest& request_data,
mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
mojom::URLLoaderClientPtr url_loader_client) {
......
BeginRequest(request_id, request_data, NULL, routing_id,
std::move(mojo_request), std::move(url_loader_client));
}
void ResourceDispatcherHostImpl::BeginRequest(
int request_id,
const ResourceRequest& request_data,
IPC::Message* sync_result, // only valid for sync
int route_id,
mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
mojom::URLLoaderClientPtr url_loader_client) {
......
ContinuePendingBeginRequest(request_id, request_data, sync_result, route_id,
headers, std::move(mojo_request),
std::move(url_loader_client), true, 0);
}
void ResourceDispatcherHostImpl::ContinuePendingBeginRequest(
int request_id,
const ResourceRequest& request_data,
IPC::Message* sync_result, // only valid for sync
int route_id,
const net::HttpRequestHeaders& headers,
mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
mojom::URLLoaderClientPtr url_loader_client,
bool continue_request,
int error_code) {
......
BeginRequestInternal(std::move(new_request), std::move(handler));
}
void ResourceDispatcherHostImpl::BeginRequestInternal(
std::unique_ptr<net::URLRequest> request,
std::unique_ptr<ResourceHandler> handler) {
......
StartLoading(info, std::move(loader));
}
void ResourceDispatcherHostImpl::StartLoading(
......
loader_ptr->StartRequest();
}
void ResourceLoader::StartRequest() {
......
if (defer_start) {
deferred_stage_ = DEFERRED_START;
} else {
StartRequestInternal();
}
}
void ResourceLoader::StartRequestInternal() {
......
request_->Start();
delegate_->DidStartRequest(this);
}
Web服務器響應了請求之後,Chromium的Net模塊會調用ResourceLoader類的成員函數OnResponseStarted,如果請求狀態返回的是OK的(eg 200),則調用StartReading
void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
......
if (request_->status().is_success())
StartReading(false); // Read the first chunk.
else
ResponseCompleted();
}
StartReading 調用ReadMore讀取Web服務器返回來的數據,保存在本地變量bytes_read
void ResourceLoader::StartReading(bool is_continuation) {
int bytes_read = 0;
ReadMore(&bytes_read);
......
if (!is_continuation || bytes_read <= 0) {
OnReadCompleted(request_.get(), bytes_read);
} else {
// Else, trigger OnReadCompleted asynchronously to avoid starving the IO
// thread in case the URLRequest can provide data synchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ResourceLoader::OnReadCompleted,
weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
}
}
void ResourceLoader::ReadMore(int* bytes_read) {
......
request_->Read(buf.get(), buf_size, bytes_read);
// No need to check the return value here as we'll detect errors by
// inspecting the URLRequest's status.
}
然後我默默的翻翻羅昇陽老師的博客,在EnsureResourceBufferIsInitialized函數中添加了堆棧打印,因爲EnsureResourceBufferIsInitialized首先檢查成員變量buffer_是否指向了一個ResourceBuffer對象,並且這個ResourceBuffer對象描述的共享內存是否已經創建。URL的加載是Render向Browser發送一個類型爲ResourceHostMsg_RequestResource的IPC消息,Browser收到這個IPC消息之後,就會通過HTTP協議請求Web服務器將網頁的內容返回來。請求得到響應後,Browser就會創建一塊共享內存,並且通過一個類型爲ResourceMsg_SetDataBuffer的IPC消息將這塊共享內存傳遞給Render進程的。
URL加載看着很繞,因爲不通線程IPC通信,你如果沒先了解下它的msg,就會看着很懵,如果只是看net 部分請求都還好。