国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

如何實(shí)現(xiàn) javascript “同步”調(diào)用 app 代碼

worldligang / 474人閱讀

摘要:同時,這個功能爆出過安全漏洞,那么,我們有沒有別的方式實(shí)現(xiàn)同步調(diào)用呢我們以為例提供一種實(shí)現(xiàn),和也可以參考。一般來說同步請求是不允許使用的,有導(dǎo)致卡頓的風(fēng)險。總結(jié)以上便是實(shí)現(xiàn)同步調(diào)用代碼的方法,其核心就是使用同步阻塞代碼,以及層攔截請求。

緣起

在 App 混合開發(fā)中,app 層向 js 層提供接口有兩種方式,一種是同步接口,一種是異步接口(不清楚什么是同步的請看這里的討論)。為了保證 web 流暢,大部分時候,我們應(yīng)該使用異步接口,但是某些情況下,我們可能更需要同步接口。同步接口的好處在于,首先 js 可以通過返回值得到執(zhí)行結(jié)果;其次,在混合式開發(fā)中,app 層導(dǎo)出的某些 api 按照語義就應(yīng)該是同步的,否則會很奇怪——一個可能在 for 循環(huán)中使用的,執(zhí)行非常快的接口,比如讀寫某個配置項(xiàng),設(shè)計(jì)成異步會很奇怪。

那么如何向 js 層導(dǎo)出同步接口呢?

問題的核心

我們知道,在 Android 框架中,通過 WebView.addJavascriptInterface() 這個函數(shù),可以將 java 接口導(dǎo)出到 js 層,并且這樣導(dǎo)出的接口是同步接口。但是在 iOS 的 Cocoa 框架中,想導(dǎo)出同步接口卻不容易,究其原因,是因?yàn)?UIWebView 和 WKWebView 沒有 addJavascriptInterface 這樣的功能。同時,Android 這個功能爆出過安全漏洞,那么,我們有沒有別的方式實(shí)現(xiàn)同步調(diào)用呢?我們以 iOS UIWebView 為例提供一種實(shí)現(xiàn),WKWebView 和 Android 也可以參考。

為了找到問題的關(guān)鍵,我們看一下 iOS 中實(shí)現(xiàn) js 調(diào)用 app 的通行方法:

首先,自定義 UIWebViewDelegate,在函數(shù) shouldStartLoadWithRequest:navigationType: 中攔截請求。

- (BOOL) webView:(UIWebView* _Nonnull)webView
????shouldStartLoadWithRequest:(NSURLRequest* _Nonnull)request
????????????????navigationType:(UIWebViewNavigationType)navigationType {
????if ([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch] != NSOrderedSame) {
????????// 不處理非 get 請求
????????return YES;
????}
?????
????NSURL* url = request.URL;
?
????if ([url.scheme isEqualToString:@"YourCustomProtocol"]) {
????????return [self onMyRequest:request];
????}
?
????return YES;
}

這種做法實(shí)質(zhì)上就是將函數(shù)調(diào)用命令轉(zhuǎn)化為 url,通過請求的方式通知 app 層,其中 onMyRequest: 是自定義的 request 響應(yīng)函數(shù)。為了發(fā)送請求,js 層要建立一個隱藏的 iframe 元素,每次發(fā)送請求時修改 iframe 元素的 src 屬性,app 即可攔截到相應(yīng)請求。

/**
?* js 向 native 傳遞消息
?* @method js_sendMessageToNativeAsync
?* @memberof JSToNativeIOSPolyfill
?* @public
?* @param str {String} 消息字符串,由 HybridMessage 轉(zhuǎn)換而來
?*/
JSToNativeIOSPolyfill.prototype.js_sendMessageToNativeAsync = function (str) {
????if (!this.ifr_) {
????????this._prepareIfr();
????}
?
????this.ifr_.src = "YourCustomProtocol://__message_send__?msg=" + encodeURIComponent(str); 
}

當(dāng) app 執(zhí)行完 js 調(diào)用的功能,執(zhí)行結(jié)果無法直接返回,為了返回結(jié)果,普遍采用回調(diào)函數(shù)方式——js 層記錄一個 callback,app 通過 UIWebView 的 stringByEvaluatingJavaScriptFromString 函數(shù)調(diào)用這個 callback(類似 jsonp 的機(jī)制)。

注意,這樣封裝的接口,天然是異步接口。因?yàn)?js_sendMessageToNativeAsync 這個函數(shù)會立即返回,不會等到執(zhí)行結(jié)果發(fā)回來。

所以,我們要想辦法把 js 代碼“阻塞”住。

同步 XMLHttpRequest

請回憶一下,js 中是用什么方法能把 UI 線程代碼“阻塞”住,同時又不跑滿 CPU?

var async = false;
var url = "http://baidu.com";
var method = "GET";
var req = new XMLHttpRequest();

req.open(method, url, async);
req.send(null);

“同步”ajax(其實(shí)沒這個詞,ajax 內(nèi)涵異步的意思)可以!在 baidu 的響應(yīng)沒返回之前,這段代碼會一直阻塞。一般來說同步請求是不允許使用的,有導(dǎo)致 UI 卡頓的風(fēng)險。但是在這里因?yàn)槲覀儾⒉粫娴娜ミh(yuǎn)端請求內(nèi)容,所以不妨一用。

至此實(shí)現(xiàn)方式已經(jīng)比較清楚了,梳理一下思路:

使用同步 XMLHttpRequest 配合特殊構(gòu)造的 URL 通知 app層。

app 層攔截請求執(zhí)行功能,將結(jié)果作為 Response 返回。

XMLHttpRequest.send() 返回,通過 status 和 responseText 得到結(jié)果。

請求攔截

那么,如何攔截請求呢?大家知道,UIWebViewDelegate 是不會攔截 XMLHttpRequest 請求的,但是 iOS 至少給了我們兩個位置攔截這類請求——NSURLCache 和 NSURLProtocol。

一、NSURLCache

NSURLCache 是 iOS 中用來實(shí)現(xiàn)自定義緩存的類,當(dāng)你創(chuàng)建了自定義的 NSURLCache 子類對象,并將其設(shè)置為全局緩存管理器,所有的請求都會先到這里檢查有無緩存(如果你沒禁掉緩存的話)。我們可以借助這個性質(zhì)攔截到接口調(diào)用請求,執(zhí)行并返回?cái)?shù)據(jù)。

- (NSCachedURLResponse*) cachedResponseForRequest:(NSURLRequest *)request {
????if ([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch] != NSOrderedSame) {
????????// 只對 get 請求做自定義處理
????????return [super cachedResponseForRequest:request];
????}
?
????NSURL* url = request.URL;
????NSString* path = url.path;
????NSString* query = url.query;
?
????if (path == nil || query == nil) {
????????return [super cachedResponseForRequest:request];
????}
?????
????LOGF(@"url = %@, path = %@, query = %@", url, path, query);
?
????if ([path isEqualToString:@"__env_get__"]) {
????????// 讀環(huán)境變量
????????return [self getEnvValueByURL:url]; //*
????} else if ([path isEqualToString:@"__env_set__"]) {
????????// 寫環(huán)境變量
????????return [self setEnvValueByURL:url];
????}
?
????return [super cachedResponseForRequest:request];
}

注意注釋有 * 號的一行,即是執(zhí)行 app 接口,返回結(jié)果。這里的結(jié)果是一個 NSCachedResponse 對象,就不贅述了。

二、NSURLProtocol

NSURLProtocol 是 Cocoa 中處理自定義 scheme 的類。這個類的使用更復(fù)雜一些,但它相比 NSURLCache 的好處是,可以使用自定義協(xié)議 scheme,防止 URL 和真實(shí) URL 混淆,并且自定義 scheme 在異步接口機(jī)制中也有使用,當(dāng)你的 app 中同時存在兩種機(jī)制時,可以使用 scheme 使得代碼更清晰。

+ (BOOL) canInitWithRequest:(NSURLRequest* _Nonnull)request {
????//只處理特定 scheme
????NSString* scheme = [[request URL] scheme];
????if ([scheme compare:@"YourCustomProtocol"] == NSOrderedSame) {
????????return YES;
????}
?
????return NO;
}
?
+ (NSURLRequest* _Nonnull) canonicalRequestForRequest:(NSURLRequest* _Nonnull)request {
????return request;
}
?
- (BirdyURLProtocol* _Nonnull) initWithRequest:(NSURLRequest* _Nonnull)request
????????????????????????????????cachedResponse:(NSCachedURLResponse* _Nullable)cachedResponse
????????????????????????????????????????client:(id _Nullable)client {
????self = [super initWithRequest:request cachedResponse:cachedResponse client:client];
????return self;
}
?
- (void) startLoading {
????NSURLRequest* connectionRequest = [self.request copy];
????NSCachedURLResponse* cachedResponse = [[YourURLCache sharedURLCache] cachedResponseForRequest:connectionRequest];
?????
????if (cachedResponse != nil) {
????????NSURLResponse* response = cachedResponse.response;
????????NSData* data = cachedResponse.data;
?
????????[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
????????[[self client] URLProtocol:self didLoadData:data];
????????[[self client] URLProtocolDidFinishLoading:self];
????} else {
????????NSError* error = [NSError errorWithDomain:@"Bad Hybrid Request"
?????????????????????????????????????????????code:400
?????????????????????????????????????????userInfo:nil];
?
????????[[self client] URLProtocol:self didFailWithError:error];
????}
}

注意,以上代碼我借用了前面 YourURLCache 的實(shí)現(xiàn),實(shí)際這是沒必要的。只是為了方便演示。

總結(jié)

以上便是實(shí)現(xiàn) javascript “同步”調(diào)用 app 代碼的方法,其核心就是使用同步 XMLHttpRequest 阻塞代碼,以及 app 層攔截請求。事實(shí)上,這個方法和操作系統(tǒng)以及開發(fā)框架無關(guān),在 Android 系統(tǒng)中,也可以實(shí)現(xiàn)這樣的機(jī)制。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79688.html

相關(guān)文章

  • 帶你玩轉(zhuǎn)小程序開發(fā)實(shí)踐|含直播回顧視頻

    摘要:小程序的視圖層目前使用作為渲染載體,而邏輯層是由獨(dú)立的作為運(yùn)行環(huán)境。比如小程序的,通信一次就像是寫情書所以,嚴(yán)格來說,小程序是微信定制的混合開發(fā)模式。出棧入棧解決小程序接口不支持的問題小程序的所有接口,都是通過傳統(tǒng)的回調(diào)函數(shù)形式來調(diào)用的。 作者:張利濤,視頻課程《微信小程序教學(xué)》、《基于Koa2搭建Node.js實(shí)戰(zhàn)項(xiàng)目教學(xué)》主編,滬江前端架構(gòu)師本文原創(chuàng),轉(zhuǎn)載請注明作者及出處 小程...

    fxp 評論0 收藏0
  • 2017年3月份前端資源分享

    平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    ermaoL 評論0 收藏0
  • 2017年3月份前端資源分享

    平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    kamushin233 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<