Mutual invocation of ios:objective-c and JavaScript
Prior to IOS7, the IOS SDK did not have the native API to provide JS calls to native code. But a delegate method of UIWebView allows us to make sure that JS needs to be called when it notifies native. After the native executes the corresponding call, the execution result can be returned to JS using the Stringbyevaluatingjavascriptfromstring method. In this way, the mutual invocation of JS and native code is realized. Specifically let JS notify native method is to let JS launch a special network request. Implemented using the load of a hidden iframe, by specifying the SRC as a special URL in Objective-c through the UIWebView webview:shouldstartloadwithrequest: Navigationtype: The method intercepts this jump and then gets the method name and parameter that JS needs to invoke by parsing the URL of the jump.
Objective-c calling JavaScript
UIWebView a method is: - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script 可以
directly invoke JS. For example, you want to get the ClientHeight property of the page document, so write:NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.documentElement.clientHeight"]];
If you want to invoke a function called XXX on the page, you only need to[webview stringByEvaluatingJavaScriptFromString:@"xxx()"]
This call has a restriction: The JS code occupies memory < 10M.
JavaScript calls Objective-c
iOS loading a Web page with a UIWebView, page loading is through the UIWebView of a delegate:uiwebviewdelegate to notify the corresponding webview. Each time you click on a link on the page (or when you load the page), a method of uiwebviewdelegate is called before loading: - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
If the return value of this method is yes, the request continues to load, and if no, it is not loaded. The secret of JavaScript calling objective C code is right here.
The first step. Match URL format
-(BOOL) WebView: (UIWebView *) WebView shouldstartloadwithrequest: (nsurlrequest *) Request Navigationtype: ( Uiwebviewnavigationtype) navigationtype{ if (request. Url.absolutestring match Urlschemepattern) { [self executesomeobjectiveccode]; return NO; Else { return YES;} }
request.URL.absoluteString match urlSchemePattern
If the URL format of the page satisfies a particular format, the request is not loaded, but the OBJECTIVE-C code is executed.
2nd Step Negotiation URL format and parameter passing method
When JavaScript wants to invoke the OBJECTIVE-C code, the JavaScript code needs to negotiate a request protocol with OBJECTIVE-C, for example: the URL scheme that is requested is "js-call://"
In this format, the JavaScript needs to call objective C code, and then the specific point, such as "Js-call://user/get" is to invoke Objective-c code in a GetUser method. If JavaScript needs to pass parameters to objective-c, the simplest way is to pass parameters like the query string for HTTP, for example: "JS-CALL://USER/SET?UID=1&NAME=JPX", Then, when parsing the URL, extract the query string and pass it to the Objective-c method. The code is as follows:
-(BOOL) WebView: (UIWebView *) WebView shouldstartloadwithrequest: (Nsurlrequest *Request Navigationtype: (uiwebviewnavigationtype) navigationtype{if([Request. Url.absolutestring Hasprefix:@"Js-call://user/set"]) {nsdictionary*parameters =[Self parsequerystring:request. Url.absoluetstring]; [Self executesomeobjectiveccodewithparameters:parameters]; returnNO; } Else if([Request. Url.absolutestring Hasprefix:@"Js-call://user/get"]) {nsdictionary*parameters =[Self parsequerystring:request. Url.absoluetstring]; [Self executesomeobjectiveccodewithparameters:parameters]; returnNO; } returnYES;}
If JavaScript needs to call several Objective C interfaces, then there will be a lot of if in the Shouldstartloadwithrequest delegate method ... else if branch code, in addition, parse query The part of the code for string is also duplicated, and the best way is to wrap it all up and define a Jpxuiwebviewjsbridge method.
self.bridge = [[JPXUIWebViewJSBridge alloc] initWithHandler:self];self.bridge.routines = @[@[@"^js-call://user/set.*$", @"setUser"], @[@"^js-call://user/get.*$", @"getUser"] ];
After defining this set of rules, it is only necessary to implement a method called Setuser in Viewcontroller - (void)setUser:(NSDictionary *)parametersFromWeb
, where Parametersfromweb is the dictionary of query string!
And then - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
you just have to write it like this:
-(BOOL) WebView: (UIWebView *) WebView shouldstartloadwithrequest: (Nsurlrequest *Request Navigationtype: (uiwebviewnavigationtype) navigationtype{nserror*error; BOOL canhandlerequest= [Self.bridge canhandlerequest:request error:&ERROR]; if(canhandlerequest) {[Self.bridge handlerequest:request error:&ERROR]; NSLog (@"error1:%@", [Error localizeddescription]); returnNO; } Else{NSLog (@"error2:%@", [Error localizeddescription]); } returnYES;}
When JavaScript calls Objective C, many people's first reaction is to write a URL call in the href on the a tag, for example: <a href="js-call://user/set?uid=1&name=jpx" >测试</a>
But such a call would have some of the following problems:
If we continuously 2 JS tune native, 2 times in a row, <a href>
in the native delegate method, can only intercept the latter request, the previous request because quickly replaced, so was ignored. There are also ways to change URLs that are less secure.
The logical approach should be to load an iframe:
function execute(url){ var iframe = document.createElement("IFRAME"); iframe.setAttribute("src", url); document.documentElement.appendChild(iframe); iframe.parentNode.removeChild(iframe); iframe = null;}
从iOS7开始,我们可以使用JavaScriptCore框架来让我们的Objective-C代码和JavaScript进行深度交互,简单的说我们可以在Objective-C代码中访问JavaScript中的变量或调用JavaScript的函数,也可以JavaScript中使用Objective-C的对象和方法
Synchronous and asynchronous
Because the IOS SDK does not natively support JS and native call each other, everyone's technical solution is their own implementation of a set of call mechanism, so there is a synchronous asynchronous problem. Careful classmate will be able to find, JS call native by inserting an IFRAME, this iframe is finished inserting, the result of execution need native another stringbyevaluatingjavascriptfromstring method to inform JS , so this is an asynchronous call.
The Stringbyevaluatingjavascriptfromstring method itself directly returns the execution result of a nsstring type, so this is obviously a synchronous call.
So JS call native is asynchronous, native call JS is synchronous. When dealing with some logic, it is unavoidable to consider this feature.
方法。
Android:java and JavaScript call each other
Before Android 4.2 can be used to addJavascriptInterface
inject native Java methods to JavaScript calls, this scheme has a certain security risk, in the page to execute some untrusted JavaScript code can control the user's phone,
So after Android 4.2 Android provides a @JavascriptInterface
way for object injection to build a binding of JavaScript objects and Android native objects, the functions provided to JavaScript calls must come with @JavascriptInterface
. This article takes @javascriptinterface as an example to explain how Android:java and JavaScript call each other.
First step: Load the local HTML file
Sometimes when we use the WebView development, we will use the local HTML file, in order to facilitate us to put the HTML files in the assets
folder, using the local loading method, do not need server support.
Define an HTML file first:
<!DOCTYPE html><html> <body> <h1>this is html</h1> </body></html>
Using file:///android_asset/index.html
load into WebView:
private void initView() { webView = (WebView) findViewById(R.id.webView); webView.loadUrl("file:///android_asset/index.html"); }
JavaScript calls Java methods
As an example of an Android toast, call the system's toast from JavaScript code.
We define a Androidtoast Java class, which has a show method for displaying toasts:
public class AndroidToast { @JavascriptInterface public void show(String str) { Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show(); } }
Need to set some parameters to WebView, open javascipt, register Javascriptinterface:
private void initView() { webView = (WebView) findViewById(R.id.webView); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setDefaultTextEncodingName("UTF-8"); webView.addJavascriptInterface(new AndroidToast(), "AndroidToast"); webView.loadUrl("file:///android_asset/index.html"); }
addJavascriptInterface
is to map the Androidtoast class to the Androidtoast object in JavaScript.
Calling Java code in javascript:
function toastClick(){ window.AndroidToast.show(‘from js‘);}
The properties of the window can be used to find the Java mapped object androidtoast, calling its Show method.
Note that the data transferred here can only be a basic data type and string, and the ability to transmit a string means that we can use the json
transport structured data.
JavaScript calls have a return value Java function
If you want to get the return value from a JavaScript-tuned method, you only need to define a method with the return value @JavascriptInterface
:
public class AndroidMessage { @JavascriptInterface public String getMsg() { return "form java"; } }
To add a JavaScript mapping WebView:
Webview.addjavascriptinterface (New Androidmessage (), "androidmessage");
JavaScript calls the Java method directly:
function showAlert(){ var str=window.AndroidMessage.getMsg(); console.log(str); }
Java calls JavaScript methods
Java in the call JS, using the WebView.loadUrl()
method, you can directly in the HTML page to execute the JavaScript method, first define a JavaScript method to Java call:
Java calls a JS function with no return value for parameters
function callFromJava(str){ console.log(str); }
Java-side invoke JavaScript method:
public void javaCallJS(){ webView.loadUrl("javascript:callFromJava(‘call from java‘)"); // 可以在loadUrl中直接给Javascript方法直接传值 }
Call JS a function with parameters that have a return value
Android before 4.4 did not provide a direct call to the JS function and get the value of the method, so before, the usual idea is the Java call JS method, the JS method executes, call Java code again to return the value.
1.Java Calling JS code
String call = "Javascript:sumtojava", Webview.loadurl (call);
2.js function processing and returns the result by calling the Java method
function Sumtojava (Number1, number2) { + number2)}
3.Java get the JS function return value in callback method
@JavascriptInterface Public void onsumresult (int result) { "Onsumresult result=" + result);}
Android 4.4 Processing
You can use Evaluatejavascript after Android 4.4. This shows a simple JS method with return value.
function getgreetings () { return 1;}
Java code is called with the Evaluatejavascript method
void Testevaluatejavascript (WebView WebView) { webview.evaluatejavascript (new valuecallback< String>() { @Override void onreceivevalue (String value) { " Onreceivevalue value= "+ value);}} );
Precautions:
- The result is defined as string, and for a simple type it is attempted to convert to a string return, and for complex data types it is recommended that JSON be returned in string form.
- The Evaluatejavascript method must be called on the UI thread (the main thread), so Onreceivevalue is also executed on the main thread.
Hybrid app Development mode, ios/android and JavaScript call each other way