美文网首页
addJavascriptInterface源码分析

addJavascriptInterface源码分析

作者: 小明写代码 | 来源:发表于2015-11-21 00:29 被阅读0次

    Java层

    我们先来看一下在代码中如何使用该功能代码如下:

    WebView myWebView = (WebView) findViewById(R.id.myWebView);
    myWebView.getSettings().setJavaScriptEnabled(true);
    myWebView.addJavascriptInterface(new Object(){                    
        @JavascriptInterface 
        public String getIMEI(){
                return ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId();
            }
        }, "Android");
    
    • 第一行代码我们获取了一个WebView的实例。
    • 第二行代码我们设置在WebView能够执行JavaScript代码。
    • 第三行我们注册一个对象到WebView,这样我们在Javascript代码中可以使用Android.getIMEI来获取手机的IMEI信息了,如var imei = Android.getIMEI()。
    揭秘addJavascriptInterface

    注册对象
    • WebView.java
      位于/frameworks/base/core/java/android/webkit/WebView.java
    private WebViewProvider mProvider;
    ...
    public void addJavascriptInterface(Object object, String name) {
        checkThread();
        mProvider.addJavascriptInterface(object, name);
    }
    

    可以看到WebView中实际是调用了WebViewProvider类中对应的addJavascriptInterface方法。

    • WebViewProvider.java
      位于/frameworks/base/core/java/android/webkit/WebViewProvider.java
    public interface WebViewProvider {
    ...
    public void addJavascriptInterface(Object obj, String interfaceName);
    public void removeJavascriptInterface(String interfaceName);
    ...
    }
    

    它只是一个接口,没有实现任何方法,那么是谁实现了该接口呢?通过分析我们知道是WebViewClassic这个类。

    • WebViewClassic.java
      位于/frameworks/base/core/java/android/webkit/WebViewClassic.java
    public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
    WebViewProvider.ViewDelegate {
    ....
      @Override
      public void addJavascriptInterface(Object object, String name) {
       if (object == null) {
           return;
       }
       WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
    
       arg.mObject = object;
       arg.mInterfaceName = name;
    
       // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
       // methods that are accessible from JS.
       if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
           arg.mRequireAnnotation = true;
       } else {
           arg.mRequireAnnotation = false;
       }
       mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
     }
    ...
    }
    

    首先构造一个JSInterfaceData变量用来存储对象和接口名信息。
    其次判断SDK的版本如果是JELLY_BEAN_MR1(也就是4.3)之上的就需要加注释@JavascriptInterfacefa,因为这里涉及到使用addJavascriptInterface接口存在一些风险。
    最后调用WebViewCore的sendMessage方法。

    • WebViewCore.java
      位于/frameworks/base/core/java/android/webkit/WebViewCore.java
    private final EventHub mEventHub;
    ...
    void sendMessage(int what, Object obj) {
        mEventHub.sendMessage(Message.obtain(null, what, obj));
    }
    

    这里的mEventHub实际上是WebViewCore.java中的一个内部类。

    public class EventHub implements WebViewInputDispatcher.WebKitCallbacks {
        // Private handler for WebCore messages.
        private Handler mHandler;
        // Message queue for containing messages before the WebCore thread is
        // ready.
        private LinkedList<Message> mMessages = new LinkedList<Message>();
    ...
        private synchronized void sendMessage(Message msg) {
            if (mBlockMessages) {
                return;
            }
            if (mMessages != null) {
                mMessages.add(msg);
            } else {
                mHandler.sendMessage(msg);
            }
        }
    ...
    }
    

    这里mMessage是一个消息队列,队列中添加的消息最终也是通过mHandle发送出去的。在该内部类中重写了Handler的handeMessage方法。

    • Handler
      /frameworks/base/core/java/android/os/Handler.java
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    final MessageQueue mQueue;
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    • MessageQueue
      /frameworks/base/core/java/android/os/MessageQueue.java
    boolean enqueueMessage(Message msg, long when) {
    ...
    if (needWake) {
            nativeWake(mPtr);
    }
    ...
    }
    
    • android_os_MessageQueue.cpp
      /frameworks/base/core/jni/android_os_MessageQueue.cpp
    static JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },
    { "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },
    { "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
    { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
    };
    
    static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {
      NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
      return nativeMessageQueue->wake();
    }
    

    NativeMessageQueue继承自MessageQueue(android_os_MessageQueue)

    在父类MessageQueue中有sp<Looper> mLooper;
    void NativeMessageQueue::wake() {
      mLooper->wake();
    }
    
    • Looper.cpp
      /frameworks/native/libs/utils/Looper.cpp
    void Looper::wake() {
      #if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ wake", this);
      #endif
    
      ssize_t nWrite;
      do {
          nWrite = write(mWakeWritePipeFd, "W", 1);
      } while (nWrite == -1 && errno == EINTR);
    
      if (nWrite != 1) {
          if (errno != EAGAIN) {
              ALOGW("Could not write wake signal, errno=%d", errno);
          }
      }
    }
    
    处理消息

    上面我们提到过内部类重写了handeMessage方法

    mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
    ...
                case ADD_JS_INTERFACE:
                           JSInterfaceData jsData = (JSInterfaceData) msg.obj;
                           mBrowserFrame.addJavascriptInterface(jsData.mObject,
                           jsData.mInterfaceName, jsData.mRequireAnnotation);
                           break;
    ...
    }
    

    这里匹配到ADD_JS_INTERFACE这一消息类型,调用BrowserFrame中的addJavascriptInterface方法

    • BrowserFrame
      /frameworks/base/core/java/android/webkit/BrowserFrame.java
    // Attached Javascript interfaces
    private Map<String, JSObject> mJavaScriptObjects;
    private Set<Object> mRemovedJavaScriptObjects;
    ...
    public void addJavascriptInterface(Object obj, String interfaceName,
            boolean requireAnnotation) {
        assert obj != null;
        removeJavascriptInterface(interfaceName);
        mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation));
    }
    

    这里是用一个JSObject对象来包装注册对象。之后调用windowObjectCleared方法

    /*
     * This method is called by WebCore to inform the frame that
     * the Javascript window object has been cleared.
     * We should re-attach any attached js interfaces.
     */
    private void windowObjectCleared(int nativeFramePointer) {
        Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
        while (iter.hasNext())  {
            String interfaceName = iter.next();
            JSObject jsobject = mJavaScriptObjects.get(interfaceName);
            if (jsobject != null && jsobject.object != null) {
                nativeAddJavascriptInterface(nativeFramePointer,
                        jsobject.object, interfaceName, jsobject.requireAnnotation);
            }
        }
        mRemovedJavaScriptObjects.clear();
    }
    

    这里通过调用JNI方法nativeAddJavascriptInterface来实现对象绑定

    • WebCoreFrameBridge
      /external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
    static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
        jobject javascriptObj, jstring interfaceName, jboolean requireAnnotation)
    {
    WebCore::Frame* pFrame = 0;
    if (nativeFramePointer == 0)
        pFrame = GET_NATIVE_FRAME(env, obj);
    else
        pFrame = (WebCore::Frame*)nativeFramePointer;
    ALOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
    
    JavaVM* vm;
    env->GetJavaVM(&vm);
    ALOGV("::WebCore:: addJSInterface: %p", pFrame);
    
    if (pFrame) {
        RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj,
                requireAnnotation);
        const char* name = getCharactersFromJStringInEnv(env, interfaceName);
        // Pass ownership of the added object to bindToWindowObject.
        NPObject* npObject = JavaInstanceToNPObject(addedObject.get());
        pFrame->script()->bindToWindowObject(pFrame, name, npObject);
        // bindToWindowObject calls NPN_RetainObject on the
        // returned one (see createV8ObjectForNPObject in V8NPObject.cpp).
        // bindToWindowObject also increases obj's ref count and decreases
        // the ref count when the object is not reachable from JavaScript
        // side. Code here must release the reference count increased by
        // bindToWindowObject.
    
        // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds
        // we use WebCore/bindings/v8/npruntime.cpp (rather than
        // WebCore/bridge/npruntime.cpp), so the function is implemented there.
        // TODO: Combine the two versions of these NPAPI files.
        NPN_ReleaseObject(npObject);
        releaseCharactersForJString(interfaceName, name);
    }
    }
    

    相关文章

      网友评论

          本文标题:addJavascriptInterface源码分析

          本文链接:https://www.haomeiwen.com/subject/xdvlhttx.html