本文只分析當主頁爲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