問(wèn)題現(xiàn)象:使用安卓手機(jī)以小程序的形式分享產(chǎn)品到微信,使用微信打開(kāi)徐许,產(chǎn)品詳情數(shù)據(jù)無(wú)法顯示。而使用iPhone分享到微信卒蘸,卻始終可以正常打開(kāi)雌隅,這個(gè)時(shí)候所有的矛頭都指向了安卓同學(xué)。
邏輯設(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é)的截圖消息:
小陳同學(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è)睁本,工作順順利利,身體健健康康忠怖。