当前我的机器还是无网络状态,以下讨论可能有错误
长按一个链接,会调用到BrowserActivity.onContextItemSelected,进而调用Controller. onContextItemSelected。在处理该菜单项时,会调用当前WebView的requestFocusNodeHref。
/**
* Request the anchor orimage element URL at the last tapped point.
* If hrefMsg is null,this method returns immediately and does not
* dispatch hrefMsg to itstarget. If the tapped point hits an image,
* an anchor, or an imagein an anchor, the message associates
* strings in named keysin its data. The value paired with the key
* may be an empty string.
*
* @param hrefMsg Thismessage will be dispatched with the result of the
* request. The message datacontains three keys:
* - "url" returns theanchor's href attribute.
* - "title" returns theanchor's text.
* - "src" returns theimage's src attribute.
*/
这里有个要注意的,Controller运行在主线程中,并且Controller有个Handler。现在Controller想去WebViewCoreThread线程中获取一些数据,它是怎么做的呢?首先在Controller处利用Controller的Handler创建一个Message,然后把这个Message(假定为Message1)作为一个数据参数,向WebViewCore发送Message(假定为Message2),即Message1是Message2的一个数据参数。WebViewCore自然可以接收到Message2并处理他,把处理后的数据放入到Message1中,然后发送出去,由于Message1是利用Controller的Handler创建的,自然Controller的Handler会在主线程中接收到这个Message1,然后就可以从Message1中获取到他最初所期望获取的数据了。
看下实际的情况,Controller在调用WebView.requestFocusNodeHref之前就利用自己的Handler创建了一个Message(Message1),该参数的what值为FOCUS_NODE_HREF并设置了一些参数信息,这个Message作为WebView.requestFocusNodeHref的参数。
WebView.requestFocusNodeHref会计算当前按下的坐标位置,然后将这些信息作为参数发送REQUEST_CURSOR_HREF消息给WebViewCoreThread线程,当然刚刚创建的Message(Message1)也作为一个参数。WebViewCoreThread线程通过三个JNI函数nativeRetrieveHref,nativeRetrieveAnchorText,nativeRetrieveImageSource获取到坐标所指向的链接的url,title,src,这个所谓的src应该是个图标吧。然后把这些信息存入Message1中再返还给主线程。
此时主线程接收到回传的Message1,Controller的Handler被调用并处理FOCUS_NODE_HREF消息。这个流程很有用,不仅仅保存链接使用了这个流程,还有些其他的涉及到获取链接的功能也走这个流程,比如拷贝链接。
在接收到消息和解出数据后,调用DownloadHandler.onDownloadStartNoStream这个静态函数。
/**
* Notify the host application a download should be done, even if there
* is a streaming viewer available for thise type.
* @param activity Activity requesting the download.
* @param url The full url to the content that should be downloaded
* @param userAgent User agent of the downloading application.
* @param contentDisposition Content-disposition http header, if present.
* @param mimetype The mimetype of the content reported by the server
* @param privateBrowsing If the request is coming from a privatebrowsing tab.
*/
这个函数是个重要的函数,负责下载资源的处理。
该函数主要做以下处理:
1. 通过url等信息获取文件名。
2. 判断SD卡是否存在。
3. 如果当前不知道mimeType则进行获取。
4. 利用DownloadManager.Request和DownloadManager进行资源的下载。
5. 显示通知信息。
这里主要看下1和3项。
其中2项采用标准判断SD卡方式,如果不存在SD卡则提示用户,并直接返回不进行下载。
4项采用DownloadManager标准的设置参数和开始下载的方式。此处需要注意的是这里会开启一个新线程来执行DownloadManager.enqueue操作。
5项目只是简单的使用Toast显示了下载通知。
1项通过静态函数URLUtil.guessFileName获取文件名
/**
* Guesses canonical filename that a download would have, using
* the URL and contentDisposition. File extension, if not defined,
* is added based on the mimetype
* @param url Url to the content
* @param contentDisposition Content-Disposition HTTP header or null
* @param mimeType Mime-type of the content or null
*
* @return suggested filename
*/
该函数通过一些分析规则和匹配规则来从url,contentDisposition,mimeType中获取到一个文件名。
3项当当前的mimeType为null是会通过FetchUrlMimeType类来获取资源实际的mimeType。
FetchUrlMimeType
/**
*This class is used to pull down the http headers of a given URL so that
* wecan analyse the mimetype and make any correction needed before we give
*the URL to the download manager.
*This operation is needed when the user long-clicks on a link or image and
* wedon't know the mimetype. If the user just clicks on the link, we will
* dothe same steps of correcting the mimetype down in
*android.os.webkit.LoadListener rather than handling it here.
*
*/
class FetchUrlMimeType extends Thread
可见FetchUrlMimeType是一个线程类,会在一个单独的线程中执行他的处理操作。这个类的处理其实就是发送一个http请求给这个资源,通过http response的头信息来获取资源实际的mimeType。http请求的处理是通过AndroidHttpClient类,HttpHost类,HttpHead类,ConnRouteParams类,HttpResponse类,Header类,这些类完成的,以上都是net或apache-http模块提供的。
如果成功的获取到实际的mimeType,会再次调用URLUtil.guessFileName获取一个文件名。
以上内容做完后,再使用DownloadManager进行下载的操作,注意这里不需要再创建一个新线程执行DownloadManager.enqueue了,因为FetchUrlMimeType本身就是个线程类,现在已经是在一个单独的线程中了。