Android源碼下的WebView分析

瀏覽器在各個平臺都有,最容易拿到源碼的就是Android的;雖然有現成的可用,還是自己分析一下;

Android的WebView.java是一個內置的支持瀏覽器的視圖View,查看源碼目錄frameworks\base\core\java\android\webkit

下面有多個java源文件,第一個爲WebView.java,這個類可不小,將近8000行;

 WebView provides no browser-like widgets, does not  enable JavaScript and web page errors are ignored. If your goal is only
 to display some HTML as a part of your UI, this is probably fine

但是該WebView只支持展示Web Page,不支持JavaScript和頁面內容;不過可以借鑑一下進行改造。

@Widget
public class WebView extends AbsoluteLayout
        implements ViewTreeObserver.OnGlobalFocusChangeListener,
        ViewGroup.OnHierarchyChangeListener {

WebView繼承AbsoluteLayout,實現OnGlobalFocusChangeListener和OnHierarchyChangeListener

構造函數

 protected WebView(Context context, AttributeSet attrs, int defStyle,
            Map<String, Object> javascriptInterfaces) {
        super(context, attrs, defStyle);
        init();//初始化

        mCallbackProxy = new CallbackProxy(context, this);//回調
        mViewManager = new ViewManager(this);//View manager
        mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces); //WebViewCore
        mDatabase = WebViewDatabase.getInstance(context);//WebViewDataBase
        mScroller = new OverScroller(context);

        updateMultiTouchSupport(context);
    }

  看看如下幾個方法

private void init() {
        setWillNotDraw(false);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setClickable(true);
        setLongClickable(true);

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        int slop = configuration.getScaledTouchSlop();
        mTouchSlopSquare = slop * slop;
        mMinLockSnapReverseDistance = slop;
        slop = configuration.getScaledDoubleTapSlop();
        mDoubleTapSlopSquare = slop * slop;
        final float density = getContext().getResources().getDisplayMetrics().density;
        // use one line height, 16 based on our current default font, for how
        // far we allow a touch be away from the edge of a link
        mNavSlop = (int) (16 * density);
        // density adjusted scale factors
        DEFAULT_SCALE_PERCENT = (int) (100 * density);
        mDefaultScale = density;
        mActualScale = density;
        mInvActualScale = 1 / density;
        mTextWrapScale = density;
        DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
        DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
        mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
        mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
        mMaximumFling = configuration.getScaledMaximumFlingVelocity();
        mOverscrollDistance = configuration.getScaledOverscrollDistance();
        mOverflingDistance = configuration.getScaledOverflingDistance();
    }

  void updateMultiTouchSupport(Context context) {
        WebSettings settings = getSettings();
        final PackageManager pm = context.getPackageManager();
        mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
                && settings.supportZoom() && settings.getBuiltInZoomControls();
        mAllowPanAndScale = pm.hasSystemFeature(
                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
        if (mSupportMultiTouch && (mScaleDetector == null)) {
            mScaleDetector = new ScaleGestureDetector(context,
                    new ScaleDetectorListener());
        } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
            mScaleDetector = null;
        }
    }

   

   一個大方法,該PrivateHandler內部多達600多行代碼

 /**
     * General handler to receive message coming from webkit thread
     */
    class PrivateHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {

  

    還有一個DataSetObserver

private class SingleDataSetObserver extends DataSetObserver {

最後面多個被JNI調用的方法

和native的接口方法

 private native int nativeCacheHitFramePointer();
    private native Rect nativeCacheHitNodeBounds();
    private native int nativeCacheHitNodePointer();
    /* package */ native void nativeClearCursor();
    private native void     nativeCreate(int ptr);
    private native int      nativeCursorFramePointer();
    private native Rect     nativeCursorNodeBounds();
    private native int nativeCursorNodePointer();
    /* package */ native boolean nativeCursorMatchesFocus();
    private native boolean  nativeCursorIntersects(Rect visibleRect);
    private native boolean  nativeCursorIsAnchor();
    private native boolean  nativeCursorIsTextInput();
    private native Point    nativeCursorPosition();
    private native String   nativeCursorText();
    /**
     * Returns true if the native cursor node says it wants to handle key events
     * (ala plugins). This can only be called if mNativeClass is non-zero!
     */
    private native boolean  nativeCursorWantsKeyEvents();
    private native void     nativeDebugDump();
    private native void     nativeDestroy();
    private native boolean  nativeEvaluateLayersAnimations();
    private native void     nativeExtendSelection(int x, int y);
    private native void     nativeDrawExtras(Canvas canvas, int extra);
    private native void     nativeDumpDisplayTree(String urlOrNull);
    private native int      nativeFindAll(String findLower, String findUpper);
    private native void     nativeFindNext(boolean forward);
    /* package */ native int      nativeFocusCandidateFramePointer();
    /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
    /* package */ native boolean  nativeFocusCandidateIsPassword();
    private native boolean  nativeFocusCandidateIsRtlText();
    private native boolean  nativeFocusCandidateIsTextInput();
    /* package */ native int      nativeFocusCandidateMaxLength();
    /* package */ native String   nativeFocusCandidateName();
    private native Rect     nativeFocusCandidateNodeBounds();
    /**
     * @return A Rect with left, top, right, bottom set to the corresponding
     * padding values in the focus candidate, if it is a textfield/textarea with
     * a style.  Otherwise return null.  This is not actually a rectangle; Rect
     * is being used to pass four integers.
     */
    private native Rect     nativeFocusCandidatePaddingRect();
    /* package */ native int      nativeFocusCandidatePointer();
    private native String   nativeFocusCandidateText();
    private native int      nativeFocusCandidateTextSize();
    /**
     * Returns an integer corresponding to WebView.cpp::type.
     * See WebTextView.setType()
     */
    private native int      nativeFocusCandidateType();
    private native boolean  nativeFocusIsPlugin();
    private native Rect     nativeFocusNodeBounds();
    /* package */ native int nativeFocusNodePointer();
    private native Rect     nativeGetCursorRingBounds();
    private native String   nativeGetSelection();
    private native boolean  nativeHasCursorNode();
    private native boolean  nativeHasFocusNode();
    private native void     nativeHideCursor();
    private native boolean  nativeHitSelection(int x, int y);
    private native String   nativeImageURI(int x, int y);
    private native void     nativeInstrumentReport();
    /* package */ native boolean nativeMoveCursorToNextTextInput();
    // return true if the page has been scrolled
    private native boolean  nativeMotionUp(int x, int y, int slop);
    // returns false if it handled the key
    private native boolean  nativeMoveCursor(int keyCode, int count,
            boolean noScroll);
    private native int      nativeMoveGeneration();
    private native void     nativeMoveSelection(int x, int y);
    private native boolean  nativePointInNavCache(int x, int y, int slop);
    // Like many other of our native methods, you must make sure that
    // mNativeClass is not null before calling this method.
    private native void     nativeRecordButtons(boolean focused,
            boolean pressed, boolean invalidate);
    private native void     nativeResetSelection();
    private native void     nativeSelectAll();
    private native void     nativeSelectBestAt(Rect rect);
    private native int      nativeSelectionX();
    private native int      nativeSelectionY();
    private native int      nativeFindIndex();
    private native void     nativeSetExtendSelection();
    private native void     nativeSetFindIsEmpty();
    private native void     nativeSetFindIsUp(boolean isUp);
    private native void     nativeSetFollowedLink(boolean followed);
    private native void     nativeSetHeightCanMeasure(boolean measure);
    private native void     nativeSetRootLayer(int layer);
    private native void     nativeSetSelectionPointer(boolean set,
            float scale, int x, int y);
    private native boolean  nativeStartSelection(int x, int y);
    private native void     nativeSetSelectionRegion(boolean set);
    private native Rect     nativeSubtractLayers(Rect content);
    private native int      nativeTextGeneration();
    // Never call this version except by updateCachedTextfield(String) -
    // we always want to pass in our generation number.
    private native void     nativeUpdateCachedTextfield(String updatedText,
            int generation);
    private native boolean  nativeWordSelection(int x, int y);
    // return NO_LEFTEDGE means failure.
    private static final int NO_LEFTEDGE = -1;
    private native int      nativeGetBlockLeftEdge(int x, int y, float scale);

 

另外一個類WebViewCore.java,代碼大概2000多行

static {
        // Load libwebcore during static initialization. This happens in the
        // zygote process so it will be shared read-only across all app
        // processes.
        try {
            System.loadLibrary("webcore");
        } catch (UnsatisfiedLinkError e) {
            Log.e(LOGTAG, "Unable to load webcore library");
        }
    }

典型的加載native的code的代碼庫webcore

看下構造行數

 public WebViewCore(Context context, WebView w, CallbackProxy proxy,
            Map<String, Object> javascriptInterfaces) {
        // No need to assign this in the WebCore thread.
        mCallbackProxy = proxy;
        mWebView = w;
        mJavascriptInterfaces = javascriptInterfaces;
        // This context object is used to initialize the WebViewCore during
        // subwindow creation.
        mContext = context;

        // We need to wait for the initial thread creation before sending
        // a message to the WebCore thread.
        // XXX: This is the only time the UI thread will wait for the WebCore
        // thread!
        synchronized (WebViewCore.class) {
            if (sWebCoreHandler == null) {
                // Create a global thread and start it.
                Thread t = new Thread(new WebCoreThread());
                t.setName(THREAD_NAME);
                t.start();
                try {
                    WebViewCore.class.wait();
                } catch (InterruptedException e) {
                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
                           "creation.");
                    Log.e(LOGTAG, Log.getStackTraceString(e));
                }
            }
        }
        // Create an EventHub to handle messages before and after the thread is
        // ready.
        mEventHub = new EventHub();
        // Create a WebSettings object for maintaining all settings
        mSettings = new WebSettings(mContext, mWebView);
        // The WebIconDatabase needs to be initialized within the UI thread so
        // just request the instance here.
        WebIconDatabase.getInstance();
        // Create the WebStorage singleton and the UI handler
        WebStorage.getInstance().createUIHandler();
        // Create the UI handler for GeolocationPermissions
        GeolocationPermissions.getInstance().createUIHandler();
        // Send a message to initialize the WebViewCore.
        Message init = sWebCoreHandler.obtainMessage(
                WebCoreThread.INITIALIZE, this);
        sWebCoreHandler.sendMessage(init);
    }

   該類中存在大量的JNI方法

  一個WebCoreThread

   private static class WebCoreThread implements Runnable {

public void run() {
            Looper.prepare();
            Assert.assertNull(sWebCoreHandler);
            synchronized (WebViewCore.class) {
                sWebCoreHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case INITIALIZE:
                                WebViewCore core = (WebViewCore) msg.obj;
                                core.initialize();
                                break;

                            case REDUCE_PRIORITY:
                                // 3 is an adjustable number.
                                Process.setThreadPriority(
                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
                                break;

                            case RESUME_PRIORITY:
                                Process.setThreadPriority(
                                        Process.THREAD_PRIORITY_DEFAULT);
                                break;
                        }
                    }
                };
                WebViewCore.class.notify();
            }
            Looper.loop();
        }

   事件處理類

 class EventHub {

 轉換消息方法

private void transferMessages() {
            mTid = Process.myTid();
            mSavedPriority = Process.getThreadPriority(mTid);

            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (DebugFlags.WEB_VIEW_CORE) {

 多達400行;

 

 WebView的官方文檔

http://developer.android.com/reference/android/webkit/WebView.html

專注設計,專注實現。

需要權限:

<uses-permission android:name="android.permission.INTERNET" />

用例1:

點擊某個按鈕或者圖片,觸發顯示對應的網頁

 Uri uri = Uri.parse("http://www.example.com"); 
 
Intent intent = new Intent(Intent.ACTION_VIEW, uri); 
 startActivity
(intent);


用例2:

 WebView控件加載指定的URL的網頁

webview.loadUrl("http://slashdot.org/"); 
 
 
// OR, you can also load from an HTML string: 
 
String summary = "<html><body>You scored <b>192</b> points.</body></html>"; 
 webview
.loadData(summary, "text/html", null);

使用案例:

創建設置WebChromeClient子類或者WebViewClient子類

修改WebSetting,setJavaScriptEnabled

在WebView中注入Java Object

addJavaScriptInterface(Object,String),該對象可以在頁面對應的context中的JavaScript所獲取到

getWindow().requestFeature(Window.FEATURE_PROGRESS); 
 
 webview
.getSettings().setJavaScriptEnabled(true); 
 
 
final Activity activity = this; 
 webview
.setWebChromeClient(new WebChromeClient() { 
   
public void onProgressChanged(WebView view, int progress) { 
     
// Activities and WebViews measure progress with different scales. 
     
// The progress meter will automatically disappear when we reach 100% 
     activity
.setProgress(progress * 1000); 
   
} 
 
}); 
 webview
.setWebViewClient(new WebViewClient() { 
   
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { 
     
Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show(); 
   
} 
 
}); 
 
 webview
.loadUrl("http://slashdot.org/");




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章