本文只分析当主页为Most Visited时的实现。
Tab.java中实现了WebViewClient的shouldInterceptRequest接口,该函数为回调函数,最终由native代码调用。调用过程为
shouldInterceptRequest() CallbackProxy.java <- shouldInterceptRequest() BrowserFrame.java <- shouldInterceptRequest() WebCoreFrameBridge.cpp
Tab.java
- public WebResourceResponse shouldInterceptRequest(WebView view,
- String url) {
- WebResourceResponse res = HomeProvider.shouldInterceptRequest(
- mContext, url);
- return res;
- }
HomeProvider
home page中的数据是从数据库中读取的,HomeProvider提供了数据。
HomeProvider.java
public static final String MOST_VISITED = "content://" + "com.android.browser.home" + "/index"; //android4.3
public static final String MOST_VISITED = "content://" + "com.android.browser.home" + "/"; //android 4.4
- public static WebResourceResponse shouldInterceptRequest(Context context,
- String url) {
- try {
- boolean useMostVisited = BrowserSettings.getInstance().useMostVisitedHomepage();
- if (useMostVisited && url.startsWith("content://")) {
- Uri uri = Uri.parse(url);
- if (AUTHORITY.equals(uri.getAuthority())) {
- InputStream ins = context.getContentResolver()
- .openInputStream(uri);
- return new WebResourceResponse("text/html", "utf-8", ins);
- }
- }
- } catch (Exception e) {}
- return null;
- }
@Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) {
- try {
- ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe();
- final ParcelFileDescriptor write = pipes[1];
- AssetFileDescriptor afd = new AssetFileDescriptor(write, 0, -1);
- new RequestHandler(getContext(), uri, afd.createOutputStream()).start();
- return pipes[0];
- } catch (IOException e) {
- Log.e(TAG, "Failed to handle request: " + uri, e);
- return null;
- }
- }
pipes[0]即为shouldInterceptRequest中的ins。
WebResourceResponse
先来看WebResourceResponse。
WebResourceResponse位于framework中,它继承了StreamLoader ,具体就不在分析了,在这里它的作用就是把文件作为一个流读出来模拟HTTP协议。
RequestHandler
数据是由RequestHandler写入HomeProvider读出的。
- public class RequestHandler extends Thread {
- private static final String TAG = "RequestHandler";
- private static final int INDEX = 1;
- private static final int RESOURCE = 2;
- private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- Uri mUri;
- Context mContext;
- OutputStream mOutput;
- static {
- sUriMatcher.addURI(HomeProvider.AUTHORITY, "/", INDEX); //android 4.4
- sUriMatcher.addURI(HomeProvider.AUTHORITY, "index", INDEX); //android 4.3
- //就是这个匹配字符串引起在android 4.4 中设置最长访问 的是主页。报出的ERR_ACCESS_DENIED 错误 同时还需要在BrowserSettings里面设置可以读取content的权限。android 4.4 默认把它给关闭了
- sUriMatcher.addURI(HomeProvider.AUTHORITY, "res/*/*", RESOURCE);
- }
- public RequestHandler(Context context, Uri uri, OutputStream out) {
- mUri = uri;
- mContext = context.getApplicationContext();
- mOutput = out;
- }
- @Override
- public void run() {
- super.run();
- try {
- doHandleRequest();
- } catch (Exception e) {
- Log.e(TAG, "Failed to handle request: " + mUri, e);
- } finally {
- cleanup();
- }
- }
- void doHandleRequest() throws IOException {
- int match = sUriMatcher.match(mUri);
- switch (match) {
- case INDEX:
- writeTemplatedIndex();
- break;
- case RESOURCE:
- writeResource(getUriResourcePath());
- break;
- }
- }
访问主页时执行writeTemplatedIndex()
- void writeTemplatedIndex() throws IOException {
- Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
- Cursor cursor = mContext.getContentResolver().query(Browser.BOOKMARKS_URI,
- new String[] { "DISTINCT url", "title", "thumbnail" },
- "(visits > 0 OR bookmark = 1) AND url NOT LIKE 'content:%' AND thumbnail IS NOT NULL", null, "visits DESC LIMIT 12");
- t.assignLoop("most_visited", new Template.CursorListEntityWrapper(cursor) {
- @Override
- public void writeValue(OutputStream stream, String key) throws IOException {
- Cursor cursor = getCursor();
- if (key.equals("url")) {
- stream.write(htmlEncode(cursor.getString(0)));
- } else if (key.equals("title")) {
- stream.write(htmlEncode(cursor.getString(1)));
- } else if (key.equals("thumbnail")) {
- stream.write("data:image/png;base64,".getBytes());
- byte[] thumb = cursor.getBlob(2);
- stream.write(Base64.encode(thumb, Base64.DEFAULT));
- }
- }
- });
- t.write(mOutput);
- }
writeTemplatedIndex()中首先从database中读取数据,
- Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
是从文件中读出HTML文件并进行解析writeValue则将从数据库中读出的数据加入解析后的数据中。
最后t.write(mOutput)则将数据写入pipes[1]中。
在BrowserSettings里面的syncStaticSettings 设置 settings.setAllowContentAccess(true);避免出现ERR_ACCESS_DENIED