OC之WebView 和原生 App 交互。
最近參加了很多面試,都問(wèn)到了關(guān)于 Hybrid 開(kāi)發(fā)。
我基本可以把 WebView 和 OC 之間的雙向交互的原理說(shuō)明白。
并且可以自己手動(dòng)的寫(xiě) js 代碼完成雙端的交互。
但由于,之前沒(méi)有用過(guò)類(lèi)似的混合開(kāi)發(fā)的框架,被面試官給一票否決了。
想想挺郁悶的。
我所理解的 Hybrid 開(kāi)發(fā)。
說(shuō)實(shí)在話,為什么要用 Hybrid 開(kāi)發(fā)模式?
- 可以動(dòng)態(tài)的更新界面,比如活動(dòng)等。App 端則基本不用做修改。
- HTML5可以寫(xiě)出很絢麗的界面。而使用 OC 原生的要寫(xiě)出這么絢麗的界面是一件很費(fèi)時(shí)費(fèi)力的過(guò)程。
說(shuō)白了,在界面表現(xiàn)靈動(dòng)性的方面,HTML5比原生的 OC 強(qiáng)大多了。代價(jià)也更小。這種情況對(duì)于 OC 一個(gè)純開(kāi)發(fā) App 界面的語(yǔ)言來(lái)說(shuō),不知道有什么想法。
從 WebView 到 OC 的數(shù)據(jù)流向。
WebView 本質(zhì)上就是一個(gè)內(nèi)置的瀏覽器。
瀏覽器的執(zhí)行過(guò)程簡(jiǎn)單來(lái)看:
- 請(qǐng)求 URL
- 服務(wù)器返回 HTML 數(shù)據(jù)
- 瀏覽器解析渲染 HTML + CSS
- 瀏覽器執(zhí)行 JS。
那么 WebView 如何跟原生的 OC 發(fā)生交互呢?
原理就在:當(dāng) WebView 這個(gè)瀏覽器發(fā)送 URL 請(qǐng)求的時(shí)候,在 OC 級(jí)別我們可以使用 WebView 的代理方法捕獲到這個(gè)信號(hào)。
**- (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType
這個(gè)代理方法返回值是 BOOL 類(lèi)型。
返回 YES 表示,請(qǐng)求繼續(xù)往下走,該干嘛干嘛。
返回 NO 表示,截獲了這個(gè)請(qǐng)求,請(qǐng)不要往下走了。也就是說(shuō),URL 請(qǐng)求,壓根就沒(méi)有發(fā)布出去。
對(duì)于 WebView 到 App 之間的交互,就是通過(guò)這個(gè)代理方法來(lái)執(zhí)行的。
簡(jiǎn)單實(shí)現(xiàn):
- 在 HTML 中,鑲嵌一個(gè) A 標(biāo)簽,給 href 屬性設(shè)置一個(gè)目的是不跳轉(zhuǎn)到百度,而是被截獲的協(xié)議鏈接。
<a href='gqs://ccccc'>從瀏覽器到 OC</a><br />
當(dāng)點(diǎn)擊 WebView 中這個(gè) A 標(biāo)簽的時(shí)候,會(huì)發(fā)送 URL 請(qǐng)求。于是進(jìn)進(jìn)入到上面的那個(gè)代理方法。
在代理方法中,過(guò)濾需要截獲的請(qǐng)求。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ([request.URL.absoluteString containsString:@"gqs"]) {
NSLog(@"%@",@"捕獲到了自定義協(xié)議");
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"這是標(biāo)題" message:@"瀏覽器到 OC 的通道打通了!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"知道了!" style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:confirmAction];
[self presentViewController:alertController animated:YES completion:nil];
return NO;
}
// 正常的URL鏈接就讓它們繼續(xù)去執(zhí)行。
return YES;
}
到此為止,WebView -> App 的流向就走通了。
原理一句話:App 通過(guò)代理方法,截獲 WebView 的請(qǐng)求,根據(jù)請(qǐng)求篩選,做一些響應(yīng)的事情。
從 App 到 WebView 的走向。
說(shuō)白了,就是一個(gè)方法。
webView stringByEvaluatingJavaScriptFromString:
這兒方法,就是在當(dāng)前的 HTML 中,插入一段 js 代碼。
你插入的是什么 js 代碼,WebView 就執(zhí)行什么代碼。
一個(gè)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景:
用戶(hù)需要從聯(lián)系人列表中,選中一個(gè)聯(lián)系人號(hào)碼(App 端)。然后把這個(gè)號(hào)碼賦值給 WebView 的某個(gè) HTML 文本框里。
也就是數(shù)據(jù)走向 : App -> WebView
做法如下:
- 在 HTML 中,實(shí)現(xiàn)定義一個(gè)用戶(hù)顯示用戶(hù)號(hào)碼的 js 函數(shù)。
<html>
<head>
<title> 標(biāo)題</title>
</head>
<body>
<a href='gqs://ccccc'>從瀏覽器到 OC</a><br />
用戶(hù)的電話號(hào)碼:<input type="text" id="userPhone" />
</body>
</html>
<script type="text/javascript">
function showUserPhoneNum(phoneNum) {
document.getElementById("userPhone").value = phoneNum;
}
</script>
- 在 App 中,選擇了用戶(hù)手機(jī)號(hào)碼之后,調(diào)用 stringByEvaluatingJavaScriptFromString 將手機(jī)號(hào)碼傳遞到 WebView 的 Input id = userPhone 這個(gè)文本框上。
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
// NSArray<CNLabeledValue<CNPhoneNumber*>*>
/**
] (
"<CNLabeledValue: 0x618000275a00: identifier=E297F1F7-CAFC-4A9D-ABF8-F79DB4496C87, label=_$!<Mobile>!$_, value=<CNPhoneNumber: 0x61800022be00: countryCode=us, digits=8885555512>>",
"<CNLabeledValue: 0x618000275d00: identifier=5E423897-5B64-4129-AF55-10B1B3153697, label=_$!<Home>!$_, value=<CNPhoneNumber: 0x61800022c280: countryCode=us, digits=8885551212>>"
)
*/
NSString *phoneNumber = [contactProperty.value stringValue];
NSLog(@"%@",phoneNumber); // 拿到用戶(hù)的電話號(hào)碼
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"showUserPhoneNum(\"%@\")",phoneNumber]];
}

從 App -> WebView 原理:一句話,通過(guò)stringByEvaluatingJavaScriptFromString調(diào)用 HTML 中已經(jīng)寫(xiě)好的,或者自己寫(xiě)的 js 代碼,來(lái)實(shí)現(xiàn)App -> WebView 的數(shù)據(jù)走向。