浏览器从界面上看都很简单,输入一个地址后就可以跳转,今天这里先整理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& obj,
const JavaParamRef& url,
jint load_url_type,
jint transition_type,
const JavaParamRef& j_referrer_url,
jint referrer_policy,
jint ua_override_option,
const JavaParamRef& extra_headers,
const JavaParamRef& j_post_data,
const JavaParamRef& base_url_for_data_url,
const JavaParamRef& virtual_url_for_data_url,
const JavaParamRef& 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("[%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 request,
int routing_id,
scoped_refptr loading_task_runner,
const GURL& frame_origin,
std::unique_ptr peer,
blink::WebURLRequest::LoadingIPCType ipc_type,
mojom::URLLoaderFactory* url_loader_factory) {
......
if (ipc_type == blink::WebURLRequest::LoadingIPCType::Mojo) {
std::unique_ptr 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 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 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 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 request,
std::unique_ptr 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 部分请求都还好。