記一次印象深刻的Bug追蹤過(guò)程

問(wèn)題現(xiàn)象:使用安卓手機(jī)以小程序的形式分享產(chǎn)品到微信,使用微信打開(kāi)徐许,產(chǎn)品詳情數(shù)據(jù)無(wú)法顯示。而使用iPhone分享到微信卒蘸,卻始終可以正常打開(kāi)雌隅,這個(gè)時(shí)候所有的矛頭都指向了安卓同學(xué)。

小程序中打開(kāi)缸沃,顯示空白

邏輯設(shè)計(jì)說(shuō)明:這里的分享數(shù)據(jù)來(lái)自H5接口恰起,通過(guò)addJavascriptInterface自定義接口完成H5和Java端的數(shù)據(jù)傳遞,產(chǎn)品ID來(lái)自后臺(tái)接口獲取趾牧。

這個(gè)時(shí)候检盼,安卓同學(xué)首先做出了響應(yīng),通過(guò)調(diào)試拿到了JS端的數(shù)據(jù)翘单,以下是這位小陳同學(xué)的截圖消息:

Android調(diào)試結(jié)果

小陳同學(xué)這個(gè)時(shí)候把問(wèn)題拋給了Web前端同學(xué)小徐吨枉,以為小徐傳遞了科學(xué)計(jì)數(shù)法的ID字符串蹦渣。

大家看小陳同學(xué)的截圖,圖中的ID是使用字符串接收的,這個(gè)時(shí)候我已經(jīng)完全排除問(wèn)題出現(xiàn)在安卓端的可能性了。于是篡石,我問(wèn)小徐,H5有對(duì)參數(shù)進(jìn)行處理嗎权逗?得到的答案如下:


大家看到圖中,我已經(jīng)給出了確定的答案冤议,認(rèn)為問(wèn)題來(lái)自于后臺(tái)斟薇。因?yàn)椋笈_(tái)同學(xué)之前的確出現(xiàn)過(guò)對(duì)ID進(jìn)行toInt處理最終轉(zhuǎn)換為負(fù)數(shù)的情況∷∷幔現(xiàn)在在傳遞時(shí)出現(xiàn)這種低級(jí)錯(cuò)誤的概率應(yīng)該也挺高的堪滨。這段話拋出去之后,團(tuán)隊(duì)炸開(kāi)了鍋蕊温,有同學(xué)認(rèn)為大家在互相推諉...

其實(shí)袱箱,還有很長(zhǎng)的截圖,這里沒(méi)有展示出來(lái)义矛。群里提到最多的一句話就是:iOS沒(méi)問(wèn)題啊发笔。就連我們的運(yùn)維同學(xué)以及UI設(shè)計(jì)同學(xué)都加入了“討伐”隊(duì)伍,種種跡象似乎都指向了安卓同學(xué)凉翻。這個(gè)時(shí)候了讨,我們的安卓同學(xué)真是“啞巴吃黃連,有苦說(shuō)不出”制轰,心里的潛臺(tái)詞肯定是:我TM的就用string接收了一下前计,我招誰(shuí)惹誰(shuí)了我!

但其實(shí)出現(xiàn)這種不知所蹤的情況垃杖,完全可以理解男杈,大家大都集中在單一平臺(tái)開(kāi)發(fā),對(duì)于其它環(huán)節(jié)的理解難免有偏差调俘。其實(shí)势就,用常識(shí)來(lái)理解這個(gè)問(wèn)題的話,的確后臺(tái)的概率比較大脉漏,前端同學(xué)對(duì)ID進(jìn)行運(yùn)算處理的概率幾乎為0,這一點(diǎn)即使是剛剛?cè)胄械男率忠膊惶赡苄溲馈6乙恢笨嗟鹊暮笈_(tái)同學(xué)卻遲遲沒(méi)有響應(yīng)侧巨,我目前始終無(wú)法確定問(wèn)題到底來(lái)自于后臺(tái)還是Web前端。直到我終于看到了下面的截圖鞭达。


這個(gè)時(shí)候司忱,我終于有九成的把握確定問(wèn)題來(lái)自于Web前端了皇忿。可是坦仍,我知道我不能明說(shuō)鳍烁。前端同學(xué)已經(jīng)在聊天記錄中給出了證據(jù),在Chrome的控制臺(tái)打印出了正常的id值繁扎,到了安卓端卻出現(xiàn)了異常幔荒。前端同學(xué)這個(gè)時(shí)候心里也有了一個(gè)定性結(jié)論,問(wèn)題來(lái)自安卓端梳玫。這個(gè)時(shí)候爹梁,我只能親自上場(chǎng),而恰好我在外面提澎,正在辦理深圳戶口姚垃,比較不便。于是盼忌,我微信給小陳發(fā)消息积糯,囑咐它把詳情頁(yè)的源碼“爬”下來(lái),我回來(lái)看看源碼谦纱。

回到家的時(shí)候看成,我問(wèn)小陳html源碼是否已經(jīng)“爬”了下來(lái),他給我發(fā)來(lái)截圖服协,我意識(shí)到前端使用了https協(xié)議绍昂,沒(méi)法獲取html源碼。于是偿荷,我想了一個(gè)辦法窘游,在源碼中嵌入一段代碼,通過(guò)代碼的形式獲取WebView產(chǎn)品詳情頁(yè)的數(shù)據(jù)跳纳。這個(gè)方法果然奏效忍饰,不一會(huì)兒,小陳就發(fā)來(lái)了頁(yè)面的html源碼寺庄。

哎喲艾蓝,我的天哪!混淆后的代碼簡(jiǎn)直不堪入目斗塘,不過(guò)還好赢织,我可以搜索方法關(guān)鍵字showShareView♀擅耍可是于置,很遺憾沒(méi)有搜索到,事件的綁定被放到了JS代碼中贞岭。在這段源碼中八毯,我注意到一個(gè)文件名已經(jīng)被混淆的JS文件搓侄,我猜想代碼應(yīng)該就在這里』八伲可是讶踪,怎樣抓到具體的方法呢?

靈機(jī)一動(dòng)泊交!我之前在代碼中讓小陳把Debug權(quán)限開(kāi)發(fā)給了H5乳讥,這次正好可以派上用場(chǎng)』詈希可是雏婶,對(duì)于混淆后的代碼,我心里依然有點(diǎn)打退堂鼓白指。

連上手機(jī)留晚,在Chrome瀏覽器中輸入chrome://inpsect,點(diǎn)擊相應(yīng)鏈接告嘲,非常順利地進(jìn)入了調(diào)試界面:

在控制臺(tái)的Source中错维,我通過(guò)關(guān)鍵詞搜索找到了混淆后的JS代碼片段,在方法名前面增加了一個(gè)斷點(diǎn)橄唬,等調(diào)試到底方法位置的時(shí)候赋焕。這個(gè)時(shí)候已經(jīng)獲取到了JS的上下文,直接通過(guò)this.gid打印出了當(dāng)前產(chǎn)品ID信息仰楚,居然是一個(gè)非常正常的整型數(shù)字隆判。大家注意,這已經(jīng)是一個(gè)在安卓端出問(wèn)題的產(chǎn)品了僧界,在JS端居然顯示是正常的侨嘀。這個(gè)時(shí)候,我的大腦非常轉(zhuǎn)動(dòng)捂襟,我的第一感覺(jué)應(yīng)該是webkit內(nèi)核看到接收的字符串全是數(shù)字做了”自以為是“的轉(zhuǎn)換咬腕。于是,我給出了團(tuán)隊(duì)如下的答案:

為了進(jìn)一步確定我的猜想葬荷,我讓小陳寫了一個(gè)簡(jiǎn)單的Demo涨共,通過(guò)JS接口傳遞一個(gè)非常大的數(shù)字字符串給Java端,看接收是否異常宠漩。不一會(huì)兒举反,我就得到了答案:


至此,我終于基本確定問(wèn)題的原因了扒吁!
猜測(cè):JS在傳遞數(shù)據(jù)給安卓端的時(shí)候照筑,應(yīng)該是使用了基本數(shù)據(jù)類型。而webkit內(nèi)核在處理的時(shí)候可能是以JS端數(shù)據(jù)類型為準(zhǔn),在傳遞到Java端時(shí)候做了轉(zhuǎn)換凝危。

為了驗(yàn)證這個(gè)猜想,我使用typeof打印id的數(shù)據(jù)類型晨逝,得到了如下結(jié)果:

于是蛾默,我告訴小徐,問(wèn)題來(lái)自于你沒(méi)有傳遞正確的數(shù)據(jù)類型給安卓端捉貌。其實(shí)這是比較危險(xiǎn)的支鸡,不同CPU可以容納的最大整型值是不一樣的。如果iOS端和安卓處理一致趁窃,也是以JS端數(shù)據(jù)類型為準(zhǔn)牧挣,只不過(guò)iOS的CPU字節(jié)寬度較大,恰好在iPhone高端機(jī)型上面沒(méi)有出現(xiàn)而低端機(jī)型出現(xiàn)的話醒陆。其實(shí)問(wèn)題依然存在瀑构,而如果iOS的確是以Native端數(shù)據(jù)類型為準(zhǔn)。這就根本不是一個(gè)問(wèn)題刨摩。但答案雖然給了團(tuán)隊(duì)寺晌,可是小徐仍然一臉狐疑,沒(méi)有經(jīng)驗(yàn)的CTO也是跟著一臉狐疑澡刹,加上解決問(wèn)題的時(shí)間較長(zhǎng)呻征。小徐在發(fā)布更新的時(shí)候也遇到了問(wèn)題,導(dǎo)致更新失敗罢浇,問(wèn)題持續(xù)陆赋,整個(gè)問(wèn)題一直在持續(xù)。

這個(gè)時(shí)候嚷闭,我告訴小徐攒岛,你發(fā)布更新后先別著急,確定更新成功后再告訴團(tuán)隊(duì)小伙伴凌受。

一直到確定更新成功阵子,我們?cè)俅螄L試分享,問(wèn)題終于引刃而解胜蛉!

問(wèn)題雖然解決了挠进,可是,安卓系統(tǒng)為什么要這樣處理呢誊册?為什么不能以Native端數(shù)據(jù)類型為準(zhǔn)呢领突?帶著這個(gè)疑問(wèn),我開(kāi)始查看安卓源碼案怯。

閱讀安卓源碼是一個(gè)痛苦的過(guò)程君旦,隨著系統(tǒng)版本的升級(jí),安卓系統(tǒng)的兼容性代碼越來(lái)越多,這給閱讀帶來(lái)了極大的困難金砍。加上安卓系統(tǒng)本身源碼量巨大局蚀,閱讀源碼就像在一個(gè)巨大的森林中尋找寶藏一樣。這個(gè)時(shí)候恕稠,其實(shí)你非常容易迷路琅绅,而我知道,只要我堅(jiān)信我想要什么鹅巍,就一定可以找到千扶。

這里我們以addJavascriptInterface這個(gè)方法作為突破口,進(jìn)入源碼:

 public void addJavascriptInterface(Object object, String name) {
        checkThread();
        mProvider.addJavascriptInterface(object, name);
    }

額骆捧,mProvider是什么鬼澎羞?難道WebView只是一個(gè)傀儡,真正處理業(yè)務(wù)的其實(shí)是mProvider敛苇?是的妆绞,沒(méi)錯(cuò)!WebView只不過(guò)是一個(gè)殼而已接谨!可是摆碉,mProvider的實(shí)現(xiàn)到底是什么呢?帶著這個(gè)疑問(wèn)脓豪,我們看到了如下mProvider實(shí)例創(chuàng)建的方法:

  private void ensureProviderCreated() {
        checkThread();
        if (mProvider == null) {
            // As this can get called during the base class constructor chain, pass the minimum
            // number of dependencies here; the rest are deferred to init().
            mProvider = getFactory().createWebView(this, new PrivateAccess());
        }
    }

    private static WebViewFactoryProvider getFactory() {
        return WebViewFactory.getProvider();
    }

又出現(xiàn)了一個(gè)工廠方法巷帝,別怕,繼續(xù)往下追蹤:
getProvider方法較長(zhǎng)扫夜,我們截取部分楞泼,看下面源碼:

static WebViewFactoryProvider getProvider() {
        synchronized (sProviderLock) {
            // For now the main purpose of this function (and the factory abstraction) is to keep
            // us honest and minimize usage of WebView internals when binding the proxy.
            if (sProviderInstance != null) return sProviderInstance;

            final int uid = android.os.Process.myUid();
            if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
                    || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
                    || uid == android.os.Process.BLUETOOTH_UID) {
                throw new UnsupportedOperationException(
                        "For security reasons, WebView is not allowed in privileged processes");
            }

            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
            try {
                Class<WebViewFactoryProvider> providerClass = getProviderClass();
                Method staticFactory = null;
                try {
                    staticFactory = providerClass.getMethod(
                        CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
                } catch (Exception e) {
                    if (DEBUG) {
                        Log.w(LOGTAG, "error instantiating provider with static factory method", e);
                    }
                }

這里的單用戶檢測(cè),安全調(diào)用之類的代碼就先忽略了笤闯。集中注意力看Provider實(shí)例創(chuàng)建的代碼堕阔,大家可以看到,這里的創(chuàng)建其實(shí)通過(guò)反射調(diào)用創(chuàng)建的颗味。這里有一個(gè)關(guān)鍵的方法getProviderClass()超陆,這個(gè)方法可能獲取到真正的Provider類對(duì)象仓蛆,跟蹤這個(gè)方法調(diào)用陷舅,我們看到了如下的調(diào)用過(guò)程:
getProviderClass() -> getWebViewProviderClass

 public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
            throws ClassNotFoundException {
        return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
                true, clazzLoader);
    }

看到了嗎彼绷?CHROMIUM_WEBVIEW_FACTORY 這才是真正的WebViewFactoryProvider類聲明解幼,跟進(jìn)這個(gè)常量:

private static final String CHROMIUM_WEBVIEW_FACTORY = "com.android.webview.chromium.WebViewChromiumFactoryProviderForO";

從命名ForO來(lái)看,這個(gè)類恰好是用于最新版本Android系統(tǒng)Oreo的炬守。沒(méi)錯(cuò)洼哎,這里我們就從最新版本的源碼入手呕屎,找到真正的問(wèn)題”元兇“磺陡。

可是趴梢,這個(gè)代碼在哪里呢漠畜?你搜索安卓源碼,根本搜索不到該類坞靶,這是為什么呢憔狞?也許你已經(jīng)猜到了,其實(shí)這段代碼就來(lái)自于Chrome核心工程 chromium滩愁。這段代碼躯喇,大家通過(guò)谷歌搜索找找看,這里我們以官方版本的代碼為準(zhǔn):
WebViewChromiumFactoryProviderForO

具體代碼很簡(jiǎn)單硝枉,如下:

package com.android.webview.chromium;
class WebViewChromiumFactoryProviderForO extends WebViewChromiumFactoryProvider {
    public static WebViewChromiumFactoryProvider create(android.webkit.WebViewDelegate delegate) {
        return new WebViewChromiumFactoryProviderForO(delegate);
    }
    protected WebViewChromiumFactoryProviderForO(android.webkit.WebViewDelegate delegate) {
        super(delegate);
    }
}

LOL,可是倦微,你以為真的很簡(jiǎn)單嗎妻味?其實(shí)不然,實(shí)現(xiàn)在父類欣福,跟進(jìn)父類责球。這個(gè)時(shí)候千萬(wàn)保持清醒,別跟丟了哦拓劝。我們想要的是Provider的創(chuàng)建過(guò)程雏逾,這個(gè)是Provider工廠類的真正類型,由它完成WebViewProvider的創(chuàng)建郑临。

如果你已經(jīng)忘了栖博,我們?cè)賮?lái)回顧一下剛剛創(chuàng)建WebViewProvider的代碼,別走神厢洞,看這里:

  private void ensureProviderCreated() {
        checkThread();
        if (mProvider == null) {
            // As this can get called during the base class constructor chain, pass the minimum
            // number of dependencies here; the rest are deferred to init().
            mProvider = getFactory().createWebView(this, new PrivateAccess());
        }
    }

看到了嗎仇让?這里拿到工廠類之后,調(diào)用了createWebView方法創(chuàng)建了Provider對(duì)象躺翻。那好辦了丧叽,我們?cè)?code>WebViewChromiumFactoryProviderForO的父類WebViewChromiumFactoryProvider直接搜索createWebView方法即可。

@Override
    public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
        return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
    }

怎么樣公你,這段代碼熟悉嗎踊淳?這里直接返回了一個(gè)WebViewChromium對(duì)象,也就是說(shuō)陕靠,WebView的所有操作迂尝,都由WebViewChromium幫忙完成。好吧懦傍,我們繼續(xù)跟進(jìn)這個(gè)類雹舀。可是跟進(jìn)這個(gè)類做什么呢粗俱?哈哈说榆,忘了吧虚吟,我們的目的是尋找addJavascriptInterface實(shí)現(xiàn)。稍等签财,容我先擦一把汗串慰。

  @Override
    public void addJavascriptInterface(final Object obj, final String interfaceName) {
        if (checkNeedsPost()) {
            mFactory.addTask(new Runnable() {
                @Override
                public void run() {
                    addJavascriptInterface(obj, interfaceName);
                }
            });
            return;
        }
        mAwContents.addJavascriptInterface(obj, interfaceName);
    }

稍微瞅一眼這個(gè)方法checkNeedsPost

 protected boolean checkNeedsPost() {
        boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
        if (!needsPost && mAwContents == null) {
            throw new IllegalStateException("AwContents must be created if we are not posting!");
        }
        return needsPost;
    }

簡(jiǎn)單理解一下,如果已經(jīng)啟動(dòng)或者調(diào)用該方法的線程不在UI線程唱蒸,則需要post到UI線程中去邦鲫,這里很明顯,我們的調(diào)用是在UI線程中神汹。因此庆捺,我們之間走下面的分支: mAwContents.addJavascriptInterface(obj, interfaceName);。那么屁魏,問(wèn)題來(lái)了滔以,AwContent又是什么鬼?在哪里創(chuàng)建的呢氓拼?

仔細(xì)查找這個(gè)類你画,我們發(fā)現(xiàn)AwContent是在initForReal方法中被創(chuàng)建的。而initForReal調(diào)用來(lái)自init方法桃漾』捣耍可是,init方法是在哪里調(diào)用的呢撬统?答案是:WebView适滓。看下面的截圖:


OK宪摧,繼續(xù)往下粒竖,看AwContent是怎么創(chuàng)建的。

 private void initForReal() {
        AwContentsStatics.setRecordFullDocument(sRecordWholeDocumentEnabledByApi
                || mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP);
        mAwContents = new AwContents(mFactory.getBrowserContextOnUiThread(), mWebView, mContext,
                new InternalAccessAdapter(), new WebViewNativeDrawGLFunctorFactory(),
                mContentsClientAdapter, mWebSettings.getAwSettings(),
                new AwContents.DependencyFactory() {
                    @Override
                    public AutofillProvider createAutofillProvider(
                            Context context, ViewGroup containerView) {
                        return mFactory.createAutofillProvider(context, mWebView);
                    }
                });
        if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
            // On KK and above, favicons are automatically downloaded as the method
            // old apps use to enable that behavior is deprecated.
            AwContents.setShouldDownloadFavicons();
        }
        if (mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
            // Prior to Lollipop, JavaScript objects injected via addJavascriptInterface
            // were not inspectable.
            mAwContents.disableJavascriptInterfacesInspection();
        }
        // TODO: This assumes AwContents ignores second Paint param.
        mAwContents.setLayerType(mWebView.getLayerType(), null);
    }

下面是一些版本兼容判斷几于,與本文探討主題無(wú)關(guān)蕊苗,先忽略。好了沿彭,看到這里朽砰,大家是不是感覺(jué)被安卓源碼忽悠的團(tuán)團(tuán)轉(zhuǎn),最開(kāi)始我們天真地以為真正的調(diào)用來(lái)自WebView喉刘,安卓系統(tǒng)告訴我們來(lái)自WebViewProvider瞧柔,我們以為這應(yīng)該就是頭了∧郎眩可是現(xiàn)在又出現(xiàn)了一個(gè)AwContent造锅。那么,它是不是真正的最終調(diào)用者呢廉邑?繼續(xù)往下看:

/**
     * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class)
     */
    @SuppressLint("NewApi")  // JavascriptInterface requires API level 17.
    public void addJavascriptInterface(Object object, String name) {
        if (TRACE) Log.i(TAG, "%s addJavascriptInterface=%s", this, name);
        if (isDestroyedOrNoOperation(WARN)) return;
        Class<? extends Annotation> requiredAnnotation = null;
        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            requiredAnnotation = JavascriptInterface.class;
        }
        mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation);
    }

我擦哥蔚,又來(lái)了一個(gè)調(diào)用對(duì)象mContentViewCore倒谷。Relax,繼續(xù)往下看糙箍,看它的實(shí)現(xiàn):

    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
            Class<? extends Annotation> requiredAnnotation) {
        if (mNativeContentViewCore != 0 && object != null) {
            mJavaScriptInterfaces.put(name, object);
            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
                    mRetainedJavaScriptObjects);
        }
    }

看方法名渤愁,nativeAddJavascriptInterface看起來(lái)最終調(diào)用來(lái)自于Native,繼續(xù)往下看:

   private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);

接下來(lái)看C++代碼深夯,這里的中間調(diào)用過(guò)程沒(méi)有深究抖格,但最終應(yīng)該是來(lái)到了這里:

static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
        jobject javascriptObj, jstring interfaceName)
{
#ifdef ANDROID_INSTRUMENT
    TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
#endif
    WebCore::Frame* pFrame = 0;
    if (nativeFramePointer == 0)
        pFrame = GET_NATIVE_FRAME(env, obj);
    else
        pFrame = (WebCore::Frame*)nativeFramePointer;
    LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
    JavaVM* vm;
    env->GetJavaVM(&vm);
    LOGV("::WebCore:: addJSInterface: %p", pFrame);
#if USE(JSC)
    // Copied from qwebframe.cpp
    JSC::JSLock lock(false);
    WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame);
    if (window) {
        JSC::Bindings::RootObject *root = pFrame->script()->bindingRootObject();
        JSC::Bindings::setJavaVM(vm);
        // Add the binding to JS environment
        JSC::ExecState* exec = window->globalExec();
        JSC::JSObject *addedObject = WeakJavaInstance::create(javascriptObj,
                root)->createRuntimeObject(exec);
        const jchar* s = env->GetStringChars(interfaceName, NULL);
        if (s) {
            // Add the binding name to the window's table of child objects.
            JSC::PutPropertySlot slot;
            window->put(exec, JSC::Identifier(exec, (const UChar *)s, 
                    env->GetStringLength(interfaceName)), addedObject, slot);
            env->ReleaseStringChars(interfaceName, s);
            checkException(env);
        }
    }
#endif  // USE(JSC)
#if USE(V8)
    if (pFrame) {
        const char* name = JSC::Bindings::getCharactersFromJStringInEnv(env, interfaceName);
        NPObject* obj = JSC::Bindings::JavaInstanceToNPObject(new JSC::Bindings::JavaInstance(javascriptObj));
        pFrame->script()->bindToWindowObject(pFrame, name, obj);
        // JavaInstanceToNPObject calls NPN_RetainObject on the
        // returned one (see CreateV8ObjectForNPObject in V8NPObject.cpp).
        // BindToWindowObject also increases obj's ref count and decrease
        // the ref count when the object is not reachable from JavaScript
        // side. Code here must release the reference count increased by
        // JavaInstanceToNPObject.
        _NPN_ReleaseObject(obj);
        JSC::Bindings::releaseCharactersForJString(interfaceName, name);
    }
#endif
}

這里的代碼量較大,我們主要關(guān)注下面這一行代碼:

 window->put(exec, JSC::Identifier(exec, (const UChar *)s, 
                    env->GetStringLength(interfaceName)), addedObject, slot);

最終數(shù)據(jù)的處理原來(lái)來(lái)自于C++端的window對(duì)象咕晋,這又是什么呢雹拄?繼續(xù)看:

    WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame);

這是在WebCore命名空間下面的JSDOMWindow對(duì)象,看到這里掌呜,其實(shí)大多數(shù)同學(xué)應(yīng)該已經(jīng)都沒(méi)有興趣看下去了办桨。這實(shí)在是一個(gè)冗長(zhǎng)的調(diào)用過(guò)程,而且在閱讀源碼過(guò)程中站辉,我們還忽略多進(jìn)程調(diào)用,忽略各種細(xì)節(jié)损姜。對(duì)此饰剥,關(guān)于這段源碼的閱讀,我們暫且告一段落摧阅,等時(shí)間充裕汰蓉,我再來(lái)補(bǔ)充。

總結(jié)

這次的問(wèn)題牽扯了移動(dòng)端棒卷、Web前端和后臺(tái)顾孽,這種跨平臺(tái)的問(wèn)題解決起來(lái)的確存在很大的困難。其實(shí)比规,我已經(jīng)很長(zhǎng)時(shí)間沒(méi)有寫JS了若厚,僅僅在幾個(gè)月前使用RN的時(shí)候有了解一些ES6的語(yǔ)法。憑借剛剛工作時(shí)僅有的2個(gè)月JS經(jīng)驗(yàn)蜒什,加上在多方面知識(shí)的累積测秸,總算順利解決了問(wèn)題。其實(shí)灾常,根據(jù)我的經(jīng)驗(yàn)來(lái)看霎冯,越是看起來(lái)無(wú)頭緒的問(wèn)題,往往越是一個(gè)極其簡(jiǎn)單的問(wèn)題钞瀑。為了避免出現(xiàn)這種問(wèn)題沈撞,在編碼過(guò)程中,必須小心翼翼雕什。盡量多檢查幾次缠俺,避免出現(xiàn)類似這樣的錯(cuò)誤显晶。另外,要嘗試接受不一樣的觀點(diǎn)晋修,如果你一開(kāi)始就接受了其他人的觀點(diǎn)吧碾,在解決問(wèn)題上就會(huì)有很強(qiáng)的目的性,解決問(wèn)題的速度也就更快墓卦。

最后倦春,新的一年里,祝大家萬(wàn)事如意落剪,闔家歡樂(lè)睁本,工作順順利利,身體健健康康忠怖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呢堰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凡泣,更是在濱河造成了極大的恐慌枉疼,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞋拟,死亡現(xiàn)場(chǎng)離奇詭異骂维,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)贺纲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門航闺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人猴誊,你說(shuō)我怎么就攤上這事潦刃。” “怎么了懈叹?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乖杠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我项阴,道長(zhǎng)滑黔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任环揽,我火速辦了婚禮略荡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歉胶。我一直安慰自己汛兜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布通今。 她就那樣靜靜地躺著粥谬,像睡著了一般肛根。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漏策,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天派哲,我揣著相機(jī)與錄音,去河邊找鬼掺喻。 笑死芭届,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的感耙。 我是一名探鬼主播褂乍,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼即硼!你這毒婦竟也來(lái)了逃片?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤只酥,失蹤者是張志新(化名)和其女友劉穎褥实,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體裂允,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡性锭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叫胖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡她奥,死狀恐怖瓮增,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哩俭,我是刑警寧澤绷跑,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站凡资,受9級(jí)特大地震影響砸捏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隙赁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一垦藏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伞访,春花似錦掂骏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)级解。三九已至初肉,卻和暖如春劈狐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疏叨。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工掩驱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芒划,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓昙篙,卻偏偏與公主長(zhǎng)得像腊状,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苔可,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評(píng)論 25 707
  • 之前發(fā)過(guò)一個(gè)帖子焚辅,但是那個(gè)帖子有點(diǎn)問(wèn)題我就重新發(fā)一個(gè)吧映屋,下面的源碼是我從今年開(kāi)始不斷整理源碼區(qū)和其他網(wǎng)站上的安卓例...
    passiontim閱讀 21,894評(píng)論 181 334
  • 連綿的陰雨 暈染著潮濕的心 北國(guó)的朔風(fēng)驅(qū)趕著 南方的喧囂 凄清的街道 裸露的眼睛在急行 積水的公路 倒映著顫抖的身...
    和風(fēng)戲雨閱讀 250評(píng)論 2 3
  • 電影在一開(kāi)始就說(shuō),楚門的世界雖然是假的同蜻,但是楚門卻是真的棚点。 楚門的世界的主人公就是楚門,從小被一家電視制作公司收養(yǎng)...
    葉小濤閱讀 491評(píng)論 0 0
  • 近幾年經(jīng)歷的很多事情湾蔓,讓我認(rèn)清和明白了很多的人和事瘫析,我簡(jiǎn)單做了個(gè)總結(jié)如下: 1,強(qiáng)大 只有你強(qiáng)大了,別人才看...
    徐偉豪閱讀 406評(píng)論 0 1