How to implement JavaScript "Sync" invoke app code

Source: Internet
Author: User

In the app mix development, the app layer provides interface to the JS layer in two ways, one is synchronous interface, one asynchronous interface (not clear what is synchronous, see the discussion here). To keep the web flowing, most of the time we should use asynchronous interfaces, but in some cases we may need to synchronize the interfaces more often. The advantage of the synchronization interface is that JS can be returned by return worth to the execution result; second, in hybrid development, some APIs exported by the app layer should be synchronized by semantics, otherwise it would be strange--a very fast interface that could be used in a for loop, such as reading and writing a configuration item, It would be strange to design asynchronously.

So how to export the synchronization interface to the JS layer?

We know that in the Android framework, through the Webview.addjavascriptinterface () function, the Java interface can be exported to the JS layer, and the exported interface is a synchronous interface. However, in the IOS Cocoa framework, it is not easy to export the synchronization interface, because UIWebView and Wkwebview do not have addjavascriptinterface features. At the same time, the Android feature has been a security breach, so we have no other way to achieve synchronous invocation? We use IOS UIWebView as an example to provide an implementation, Wkwebview and Android can also refer to.

To find the key to the problem, let's take a look at the ways in which the JS call app is implemented in IOS:

First, customize the uiwebviewdelegate to intercept the request in function Shouldstartloadwithrequest:navigationtype:.

-(BOOL) WebView: (uiwebview* _nonnull) WebView    shouldstartloadwithrequest: (nsurlrequest* _nonnull) Request                Navigationtype: (uiwebviewnavigationtype) Navigationtype {    if ([Request. HttpMethod compare:@ "get" options:nscaseinsensitivesearch]! = nsorderedsame) {        //Do not process non-GET request        return YES;        nsurl* url = Request. URL;    if ([Url.scheme isequaltostring:@ ' Yourcustomprotocol ']) {        return [self onmyrequest:request];    }    return YES;}

This practice essentially translates the function call command into a URL and notifies the app layer by request, where Onmyrequest: is a custom request response function. In order to send a request, theJS layer to create a hidden IFRAME element , each time the request is sent to modify the SRC attribute of the IFRAME element, the app can intercept the corresponding request.

/** * JS Pass message to Native * @method Js_sendmessagetonativeasync * @memberof jstonativeiospolyfill * @public * @param str {str ING} message string, converted from hybridmessage to */jstonativeiospolyfill.prototype.js_sendmessagetonativeasync = function (str) {if (! THIS.IFR_) {THIS._PREPAREIFR ();} THIS.IFR_.SRC = ' yourcustomprotocol://__message_send__?msg= ' + encodeuricomponent (str); }

When the app executes the function of JS call, the execution result cannot be returned directly, in order to return the result, the callback function is commonly used to record a callback,app through the uiwebview of the--JS layer. The stringbyevaluatingjavascriptfromstring function calls this callback (a mechanism similar to JSONP).

Note that such encapsulated interfaces are naturally asynchronous interfaces. Because Js_sendmessagetonativeasync this function will return immediately, will not wait until the execution results are sent back.

So, we have to find a way to "block" JS code to live.

Please recall, JS in what way can the UI thread code "block", while not running full CPU?

var async = False;var url = ' http://baidu.com '; var method = ' GET ';
var req = new XMLHttpRequest ();
Req.open (method, URL, async);
Req.send (NULL);

"Synchronous" Ajax (in fact not the word, Ajax meaning asynchronous meaning) can! This code is blocked until Baidu's response is not returned. In general, synchronization requests are not allowed, and there is a risk that the UI may lag. But here because we do not really go to the remote request content, so it may be useful.

At this point the implementation of the way has been relatively clear, comb the idea:

    1. Notifies the app layer with a URL that synchronizes with a specially constructed xmlhttprequest.
    2. The app layer intercepts the request execution function and returns the result as Response.
    3. Xmlhttprequest.send () returns, resulting from status and ResponseText.

So, how do you intercept requests? As you know, Uiwebviewdelegate does not intercept XMLHttpRequest requests, but IOS gives us at least two locations to intercept such requests--nsurlcache and Nsurlprotocol.

Nsurlcache is a class used in IOS to implement a custom cache, when you create a custom Nsurlcache subclass object and set it as the global cache manager, all requests come here first to check if there is no cache (if you have not disabled the cache). This nature allows us to intercept an interface call request, execute and return data.

-(nscachedurlresponse*) Cachedresponseforrequest: (Nsurlrequest *) Request {    if ([request]. HttpMethod compare:@ "get" options:nscaseinsensitivesearch]! = nsorderedsame) {        //Only custom processing of GET requests        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__"]) {        //Read environment variable        return [self getenvvaluebyurl:url];//*    } else if ( [Path isequaltostring:@ "__env_set__"]) {        //write environment variable        return [self setenvvaluebyurl:url];    }    return [Super Cachedresponseforrequest:request];}

Note that the comment has a line of *, which is to execute the app interface and return the result. The result here is a Nscachedresponse object, do not repeat it.

Second, Nsurlprotocol is the class that handles custom scheme in Cocoa. This class is more complex to use, but it has the advantage of using a custom protocol scheme to prevent URLs from being confused with real URLs, and custom scheme is also used in the asynchronous interface mechanism, and when there are two mechanisms in your app, you can use the  nsurlcache. You can use scheme to make your code clearer.

+ (BOOL) Caninitwithrequest: (nsurlrequest* _nonnull) Request {//Processing only specific scheme nsstring* scheme = [[Request URL] Schem    E];    if ([Scheme compare:@ "yourcustomprotocol"] = = Nsorderedsame) {return YES; } return NO; + (nsurlrequest* _nonnull) canonicalrequestforrequest: (nsurlrequest* _nonnull) request {return request;} -(birdyurlprotocol* _nonnull) initwithrequest: (nsurlrequest* _nonnull) Request Cachedrespon SE: (nscachedurlresponse* _nullable) cachedresponse client: (id<nsurlprotocolclient    > _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]; }}

Note that the above code I borrowed the implementation of Yoururlcache, actually this is not necessary. Just for the convenience of demo.

The above is the way to implement the JavaScript "sync" call app code, the core of which is the use of synchronous XMLHttpRequest blocking code, as well as the app layer interception request. In fact, this approach is independent of the operating system and the development framework, which can be implemented in Android systems.

How to implement JavaScript "Sync" invoke app code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.