本文已獨(dú)家授權(quán) 郭霖 ( guolin_blog ) 公眾號(hào)發(fā)布店雅!
周一清早咆霜,某技術(shù)(對(duì)接我司業(yè)務(wù)SDK的技術(shù))在有我司boss的微信群火急火燎地艾特我說(shuō)為什么H5的回調(diào) SDK 這邊收不到籍铁?看到消息的我內(nèi)心瞬間那是焦慮的一P狐树,飛奔公司打開(kāi)電腦雙擊IDE涮母,心想別人用的好好的挟冠,這不太科學(xué)呀鹏漆。
說(shuō)起H5與Android的交互巩梢,簡(jiǎn)單來(lái)說(shuō)就是android這邊提供對(duì)象,暴露方法讓H5的腳本文件進(jìn)行調(diào)用艺玲,但是如果出現(xiàn)交互失敗的情況括蝠,那就需要逐一分析挨個(gè)排除。本篇文章只針對(duì)原生Webview進(jìn)行討論板驳,市面上一些封裝好又跛、優(yōu)秀的第三方Webview這里就不涉及了。
定位一:Http 和 Https 混合資源加載
由于H5目前的大開(kāi)發(fā)環(huán)境若治,一個(gè)Js文件(最終會(huì)被 html引用)可能會(huì)出現(xiàn)兩種不同混合的資源(尤其是H5頁(yè)游慨蓝,頁(yè)游會(huì)使用大量的混合資源),比如 Http和https資源端幼。從Android5.0開(kāi)始礼烈,WebView默認(rèn)不支持同時(shí)加載Https和Http混合模式。如果Webview不允許混合加載婆跑,那么js文件可能就不會(huì)生效此熬,js文件異常,就可能會(huì)導(dǎo)致H5無(wú)法調(diào)用Android滑进;實(shí)際上犀忱,我們?cè)谧远xWebview時(shí)一般都允許它混合加載
WebSettings.setMixedContentMode.(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
WebSettings.setMixedContentMode()這個(gè)方法,也就是加載內(nèi)容的混合模式扶关,谷歌提供的混合模式一共有如下三種模式
MIXED_CONTENT_NEVER_ALLOW
這種模式代表的意思是指:Webview不允許一個(gè)安全的站點(diǎn)(https)去加載非安全的站點(diǎn)內(nèi)容(http),比如阴汇,https網(wǎng)頁(yè)內(nèi)容的圖片是http鏈接。MIXED_CONTENT_ALWAYS_ALLOW
這種模式代表的意思是指:WebView是可以在一個(gè)安全的站點(diǎn)(Https)里加載非安全的站點(diǎn)內(nèi)容(Http), 這是WebView最不安全的操作模式节槐,但是畢竟通用搀庶,也就是一了百了的寫(xiě)法MIXED_CONTENT_COMPATIBILITY_MODE
這個(gè)模式比較有趣:當(dāng)涉及到混合式內(nèi)容時(shí)拐纱,WebView會(huì)嘗試去兼容最新Web瀏覽器的風(fēng)格。一些不安全的內(nèi)容(Http)能被加載到一個(gè)安全的站點(diǎn)上(Https)哥倔,而其他類(lèi)型的內(nèi)容將會(huì)被阻塞秸架。這些內(nèi)容的類(lèi)型是被允許加載還是被阻塞,可能會(huì)隨著版本的不同而改變咆蒿,官網(wǎng)并沒(méi)有給出明確的定義东抹。這種模式主要用于在A(yíng)pp里面不能控制內(nèi)容的渲染,但是又希望在一個(gè)安全的環(huán)境下運(yùn)行蜡秽。
綜上:如果想一了百了(簡(jiǎn)單快捷)設(shè)置府阀,直接WebSettings.MIXED_CONTENT_ALWAYS_ALLOW即可
定位二:WebSettings基本配置
WebSettings,這個(gè)抽象類(lèi)主要是用來(lái)管理WebView的狀態(tài)芽突,開(kāi)發(fā)者主動(dòng)對(duì)WebSettings進(jìn)行配置试浙,可以豐富WebView的功能以至于來(lái)滿(mǎn)足開(kāi)發(fā)需求。如果想通過(guò)Webview來(lái)實(shí)現(xiàn)Android與H5的交互寞蚌,需要對(duì)WebSettings進(jìn)行配置
首先:實(shí)例化WebSettings ( 通過(guò)Webview.getSettings( )來(lái)獲取 ):
WebSettings settings = webView.getSettings();
接著:設(shè)置WebView是否允許執(zhí)行JavaScript腳本田巴,默認(rèn)false,false為不允許挟秤,因此要將其手動(dòng)設(shè)置為true
settings.setJavaScriptEnabled(true);
然后:還有一個(gè)API也比較重要:
settings.setDomStorageEnabled(true);
這個(gè)API的用法壹哺,是否開(kāi)啟本地DOM存儲(chǔ)。理論上對(duì)應(yīng)Html5中的localStorage(可以使用Android4.4手機(jī)和Chrome Inspcet Device聯(lián)調(diào)艘刚,關(guān)于這個(gè)聯(lián)調(diào)管宵,后文會(huì)講),用于持久化的本地存儲(chǔ)攀甚,除非主動(dòng)刪除數(shù)據(jù)箩朴,否則數(shù)據(jù)是永遠(yuǎn)不會(huì)過(guò)期的,因?yàn)槎鄶?shù)瀏覽器都支持 localStorage 的秋度,但是鑒于它的安全特性(任何人都能讀取到它炸庞,盡管有相應(yīng)的限制,將敏感數(shù)據(jù)存儲(chǔ)在這里依然不是明智之舉)荚斯,Android 默認(rèn)是關(guān)閉該功能的埠居,也就是false。但是事期,我們一般會(huì)將其設(shè)置true滥壕。
定位三:Android與H5交互之代碼細(xì)節(jié)
Android與H5的交互,實(shí)際是通過(guò)注解和對(duì)象兽泣、方法來(lái)完成的绎橘,
首先,Webview調(diào)用addJavascriptInterface方法撞叨,在立馬傳入具體的參數(shù)允許被H5調(diào)用金踪,系統(tǒng)源碼如下:
webView.addJavascriptInterface( Object object, String name)
參數(shù)一:具體的對(duì)象,也就是H5與Android交互的實(shí)體類(lèi)
參數(shù)二:參數(shù)一對(duì)象的實(shí)例化對(duì)象牵敷,也就是給前端調(diào)用的類(lèi)名
具體使用胡岔,參考代碼如下:
A: 傳入類(lèi)和對(duì)象名(對(duì)象名與H5需要協(xié)商好),如果對(duì)象名不一致枷餐,H5無(wú)法調(diào)用Android的原生方法 靶瘸,而且要仔細(xì)區(qū)分大小寫(xiě)、l(小寫(xiě)的L)與I(大寫(xiě)的i)這些類(lèi)似的字符等等(稍微不注意就寫(xiě)錯(cuò)毛肋,一寫(xiě)錯(cuò)就無(wú)法調(diào)用....)
webView.addJavascriptInterface(new H5CallBackAndroid(), "callBack");
B: Android 對(duì)象類(lèi)的編寫(xiě)(參考)
public class H5CallBackAndroid {
@JavascriptInterface
public void useHtmlData(String data) {
if (data != null && data.length()>0){
Log.i("info", "H5傳過(guò)來(lái)的數(shù)據(jù) :"+data);
// Android 原生處理data
}else {
Log.i("info", "H5傳過(guò)來(lái)的數(shù)據(jù) 無(wú)效:");
}
}
}
注意:
H5調(diào)Android使用的方法怨咪,需要在方法上用 @JavascriptInterface注解來(lái)定義該方法;
被該注解修飾的方法權(quán)限需要聲明為 public 這點(diǎn)尤其注意
定位四:SSL
由于HTTPS協(xié)議通過(guò)SSL進(jìn)行通信润匙,當(dāng)使用HTTPS通信的url在Webview出現(xiàn)錯(cuò)誤時(shí)诗眨,一般會(huì)通過(guò)onReceivedSslError回調(diào)通知,常見(jiàn)的做法是直接忽略孕讳,讓W(xué)ebview繼續(xù)加載
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.i("log", "shouldOverrideUrlLoading: url"+url);
return super.shouldOverrideUrlLoading(view, url);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//繼續(xù)加載
handler.proceed();
}
});
這里的 SslErrorHandler:也就是當(dāng)前處理錯(cuò)誤的Handler匠楚,它只有兩個(gè)函數(shù):
- SslErrorHandler.proceed()
- SslErrorHandler.cancel()
其中,SslErrorHandler.proceed()表示忽略錯(cuò)誤繼續(xù)加載厂财;SslErrorHandler.cancel()表示取消加載芋簿。在onReceivedSslError的默認(rèn)實(shí)現(xiàn)中是使用的SslErrorHandler.cancel()來(lái)取消加載,所以一旦出來(lái)SSL錯(cuò)誤璃饱,HTTPS網(wǎng)站就會(huì)被取消加載与斤、,如果想忽略錯(cuò)誤繼續(xù)加載就只有重寫(xiě)onReceivedSslError荚恶,并調(diào)用SslErrorHandler.proceed()繼續(xù)加載
定位五:混淆
如果項(xiàng)目開(kāi)啟了混淆撩穿,也就是minifyEnabled設(shè)置為了true
buildTypes {
//。裆甩。冗锁。省略
release {
//開(kāi)啟混淆
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
但是,如果沒(méi)有配置Webview的混淆規(guī)則嗤栓,也是無(wú)法與H5正常交互的冻河,這點(diǎn)非常重要!\运А叨叙!
關(guān)于Webview常見(jiàn)的混淆規(guī)則如下:
#保留annotation, 例如 @JavascriptInterface 等 annotation
-keepattributes *Annotation*
#保留跟 javascript相關(guān)的屬性
-keepattributes JavascriptInterface
#保留JavascriptInterface中的方法
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
#這個(gè)類(lèi)是用來(lái)與js交互堪澎,所以這個(gè)類(lèi)中的 字段 擂错,方法, 不能被混淆樱蛤、全路徑名稱(chēng).類(lèi)名
-keepclassmembers public class com.youpackgename.xxx.H5CallBackAndroid{
<fields>;
<methods>;
public *;
private *;
}
更多的混淆細(xì)則钮呀,可自行通過(guò)瀏覽器了解查閱詳細(xì)的混淆配置信息
定位六:前端兄弟的問(wèn)題
如果前端兄弟寫(xiě)錯(cuò)了或者弄錯(cuò)了(小寫(xiě)的c寫(xiě)成大寫(xiě)的C剑鞍、l(小寫(xiě)的L)與I(大寫(xiě)的i)等等),出現(xiàn)這種情況當(dāng)然是無(wú)法調(diào)用到android原生的方法呀爽醋,所以必要的時(shí)候也可以找他幫忙一起看看
定位七:Android9.0新特性
由于從Android 9.0(API級(jí)別28)開(kāi)始蚁署,系統(tǒng)內(nèi)部默認(rèn)禁用明文支持(也就是明面上不支持http),因此http的url均無(wú)法在webview中加載蚂四。(這種情況是針對(duì)9.0系統(tǒng) webview打不開(kāi)的情況光戈,特此做補(bǔ)充說(shuō)明)
解決辦法
- 在A(yíng)ndroidManifest.xml文件中的Application標(biāo)簽添加android:usesCleartextTraffic="true",參考代碼如下:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".tools.App"
android:roundIcon="@mipmap/ic_launcher_round"
<!--讓9.0 webview 支持htp-->
android:usesCleartextTraffic="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
補(bǔ)充:H5與Android的調(diào)試
關(guān)于H5與Android的聯(lián)合調(diào)試遂赠,下面做個(gè)簡(jiǎn)單說(shuō)明:
準(zhǔn)備工作:
- PC下載并安裝chrome(谷歌)瀏覽器
- 一臺(tái)安卓手機(jī)(4.4系統(tǒng)以上)久妆,用usb線(xiàn)鏈接電腦,打開(kāi)開(kāi)發(fā)者模式跷睦,且允許WebView進(jìn)行調(diào)試筷弦,需新增如下代碼:
WebView.setWebContentsDebuggingEnabled(true);
編譯并運(yùn)行代碼
- chrome瀏覽器地址欄輸入 chrome://inspect,進(jìn)入后點(diǎn)擊 inspect 即進(jìn)入調(diào)試模式(需要翻(越)墻(壁))
在A(yíng)PP上顯示具體的H5界面后抑诸,該調(diào)試界面才會(huì)顯示需要調(diào)試的包名等具體信息奸笤。點(diǎn)擊inspect后,就會(huì)進(jìn)入調(diào)試界面哼鬓。一番操作后你會(huì)發(fā)現(xiàn)在網(wǎng)頁(yè)上操作监右、和手機(jī)操作是同步進(jìn)行的
查看請(qǐng)求體、響應(yīng)體該如何操作异希?
一番操作后(如:用戶(hù)登錄)健盒,如下圖,點(diǎn)擊all 紅色箭頭称簿,然后看一下藍(lán)色方框內(nèi)的是不是很熟悉扣癣,當(dāng)然這里只是簡(jiǎn)單介紹如何快速使用以供參考。
如果這篇文章對(duì)您有開(kāi)發(fā)or學(xué)習(xí)上的些許幫助憨降,希望各位看官留下寶貴的star父虑,謝謝。
Ps:著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明作者, 商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)授药,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處(開(kāi)頭或結(jié)尾請(qǐng)?zhí)砑愚D(zhuǎn)載出處士嚎,添加原文url地址),文章請(qǐng)勿濫用,也希望大家尊重筆者的勞動(dòng)成果