作為誓死效忠大安卓帝國的程序狗,我一般不寫技術(shù)類文章咳蔚。
你們翻翻我的文章就會發(fā)現(xiàn)豪嚎,我還真不像技術(shù)流的。
不過最近搞一個安卓的坑谈火,搞得非常蛋疼侈询,于是打算紀(jì)錄下來。
坑是這樣的:我有一個WebView糯耍,里面有一個寫字區(qū)域扔字,然后我要在寫字的同時呼叫里面的JS。
在iOS上這事挺容易整的温技,比如這樣(obj-C為例):
NSString *hasRange =
[self stringByEvaluatingJavaScriptFromString:@"MobileWriter.hasRange()"];
但在大安卓帝國革为,這事就有點(diǎn)蛋疼了。
在KitKat上舵鳞,WebView有個接口名叫evaluateJavascript震檩,從而事情是這樣的:
evaluateJavascript("MobileWriter.hasRange();", resultCallback);
這貨看上去和iOS上差不多,但已經(jīng)有點(diǎn)討厭了:它是Callback機(jī)制的蜓堕,不像iOS上直接拿結(jié)果抛虏。
但,這還算好的套才,如果不是KitKat迂猴,也就是4.4之前的Android,你連這個接口都沒有背伴,于是只能這樣:
loadUrl("javascript:MobileWriter.hasRange();");
這個就很蛋疼了沸毁,因?yàn)闆]有callback儡率,你必須在JS運(yùn)行結(jié)束后,讓JS去調(diào)用一個指定的對象以清,從而通過這個對象來獲得回調(diào)儿普,比如下面這樣:
(In Java)
addJavascriptInterface(new JavascriptDelegate () {
@JavascriptInterface
public void jsCallback (String result) {
Log.i("Editor", "Blablabla...");
}
}, "AndroidHost");
(In Javascript)
MobileWriter.hasRange = function () {
"Blablabla..."
if (AndroidHost && AndroidHost.jsCallback) {
AndroidHost.jsCallback('Mission Complete.');
}
};
看著是不是就很蛋疼?
但掷倔,這根本不算事眉孩,挺Easy的,只要J-J兩端協(xié)議定好勒葱,這都不叫事兒浪汪。
麻煩的是下面這個問題:
每次你在Java端使用loadUrl的時候,在4.4以下的安卓上都會引發(fā)WebView的頁面重新載入事件(而且這個你還沒法通過重載WebViewClient的shouldOverrideUrlLoading方法來阻止)凛虽,從而引發(fā)系統(tǒng)的clearHelpers死遭,這貨則會調(diào)用clearTextEntry并最終調(diào)用到hideSoftKeyboard。
這個貌似看上去沒什么凯旋,但實(shí)際上卻很糟糕呀潭,因?yàn)檫@會導(dǎo)致兩個問題:
- SoftKeyboard會自動消失(hideSoftKeyboard);
- Contextual Menu和相關(guān)選區(qū)會自動消失(clearTextEntry)至非。
也就是說钠署,如果你在輸入的時候就要調(diào)用JS的話,那么只要你調(diào)用了JS荒椭,輸入狀態(tài)就自動消失谐鼎,鍵盤沒了,選區(qū)沒了趣惠,你得重新開始選擇狸棍。
這事就很蛋疼了。
4.4為什么通過調(diào)用loadUrl來調(diào)用JS不會有這個問題味悄?因?yàn)槿绻阏{(diào)用的是javascript協(xié)議從而也就是調(diào)用js函數(shù)的話草戈,其實(shí)4.4下面走的是上面提到的evaluateJavascrpt,當(dāng)然安全了傍菇。
解決這個問題的方法猾瘸,一個是用反射調(diào)用android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(),這個比較蛋疼丢习。
另一個,則是設(shè)法通知JS我Java端有事件了淮悼,然后讓JS調(diào)用JavascriptDelegate插入的Delegate對象咐低,并從這個對象獲取當(dāng)前要做的事件,并執(zhí)行袜腥。
第一個方法比較霸氣见擦,直接用反射钉汗,相當(dāng)犀利,但我不確定能否通過安檢(不過國內(nèi)App反正也沒啥檢查鲤屡,應(yīng)該不慌损痰。GoogleAppStore是否允許我這么玩我就不知道了)。
第二個方法比較溫和酒来,沒這么霸氣卢未,但缺點(diǎn)是你得加一個同步鎖,避免操作不同步導(dǎo)致問題——WebView中的JS是跑在另一根線程上的堰汉,這種頻繁的線程間相互調(diào)用回調(diào)的方法安全性是個問題辽社。
至于說通知JS應(yīng)該要召喚Delegate的方法嘛,當(dāng)然不能傻傻地用loadUrl了翘鸭。你可以小小地微調(diào)一下WebView的尺寸滴铅,引發(fā)JS端的window.onresize事件,然后就可以讓JS端去調(diào)用Java端了就乓。
或者另一個比較蛋疼的方法是JS端開一個Timeout甚至Interval汉匙,這個有點(diǎn)網(wǎng)站開發(fā)早期的輪詢了,但個人不建議這么做生蚁,畢竟是手機(jī)端盹兢,畢竟是輪詢,還是要考慮資源消耗的守伸。
至于說有沒有別的通知手段绎秒,這個暫時沒想到。尼摹。见芹。