• home > OS > IOS > Develop >

    JavaScript与原生APP(Android/iOS)之间相互调用通信总结

    Author:zhoulujun Date:

    Android与js互相调用对于Android调用JS代码的方法有2种:通过WebView的loadUrl()通过WebView的evaluateJavascript()对于JS调用Android

    Android与js互相调用

    对于Android调用JS代码的方法有2种:

    1. 通过WebView的loadUrl()

    2. 通过WebView的evaluateJavascript()

    对于JS调用Android代码的方法有3种:

    • 通过WebView的addJavascriptInterface()进行对象映射:漏洞问题:你不知道的 Android WebView 使用漏洞

    • 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url

    • 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

    Android与JavaScript相互调用


    Android通过WebView与JS的交互方式进行了全面介绍

    安卓调用js方法:

    webView = findViewById(R.id.webview);

    webView.getSettings().setJavaScriptEnabled(true);

    webView.loadUrl("file:///android_asset/show.html");

    定义按钮的点击事件:

    Button btn = findViewById(R.id.btn);
    
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            testJS();
        }
    });

    其中testJS代码为:

    @SuppressLint("SetJavaScriptEnabled")
    public void testJS() {
        webView.loadUrl("javascript:test()");
    }

    据此,就实现了安卓调用js方法。

    js调用安卓方法:

    需要在activity中定义被调用的方法:

    @JavascriptInterface
    public void hello(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    并且需要给webview绑定上java对象:

    webView.addJavascriptInterface(this, "justTest");

    最后,在js中调用该方法:

     <button onclick="justTest.hello('js调用安卓方法!')">调用安卓方法</button>


    具体案例,参考《android 调用js,js调用android

    IOS与JavaScript之间的调用

    原理其实差不多

    JS调用原生OC篇

    UIWebView的代理方法拦截这次请求,然后再做相应的处理。

    loadURL("firstClick://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址");

    1. JS中的firstClick,在拦截到的url scheme全都被转化为小写。

    2. html中需要设置编码,否则中文参数可能会出现编码问题。

    3. JS用打开一个iFrame的方式替代直接用document.location的方式,以避免多次请求,被替换覆盖的问题。

    早期的JS与原生交互的开源库很多都是用得这种方式来实现的,例如:PhoneGap、WebViewJavascriptBridge

    JavaScriptCore

    在iOS 7之后,apple添加了一个新的库JavaScriptCore,用来做JS交互,因此JS与原生OC交互也变得简单了许多。

    JavaScriptCore 大体是由 4 个类以及 1 个协议组成的

    • JSContext 是 JS 执行上下文,你可以把它理解为 JS 运行的环境。

    • JSValue 是对 JavaScript 值的引用,任何 JS 中的值都可以被包装为一个 JSValue。

    • JSManagedValue 是对 JSValue 的包装,加入了“conditional retain”。

    • JSVirtualMachine 表示 JavaScript 执行的独立环境。

    还有 JSExport 协议:实现将 Objective-C 类及其实例方法,类方法和属性导出为 JavaScript 代码的协议。

    导入JavaScriptCore库, 然后在OC中获取JS的上下文

    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    可能最新版本的iOS系统做了改动,现在(iOS9,Xcode 7.3,去年使用Xcode 6 和iOS 8没有线程问题)中测试,block中是在子线程,因此执行UI操作,控制台有警告,需要回到主线程再操作UI。

    推荐阅读《iOS 与 JS 交互开发知识总结

    iOS Native 调用 JS

    iOS Native 调用 JS 的实现方法也被 JavaScriptCore 划分开来:

    • webview 直接注入 JS 并执行

    • JavaScriptCore 方法

    在 iOS 平台,webview 有注入并执行 JS 的 API。

    UIWebView

    UIWebView 有直接注入 JS 的方法:

    NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"这里是JS中alert弹出的message"];
    [_webView stringByEvaluatingJavaScriptFromString:jsStr];

    注意:该方法会同步返回一个字符串,因此是一个同步方法,可能会阻塞UI。方法会返回运行 JS 的结果(nullable NSString *),它是一个同步方法,会阻塞当前线程!尽管此方法不被弃用,但最佳做法是使用 WKWebView 类的 evaluateJavaScript:completionHandler:method。

    WKWebView

    不同于 UIWebView,WKWebView 注入并执行 JS 的方法不会阻塞当前线程。因为考虑到 webview 加载的 web content 内 JS 代码不一定经过验证,如果阻塞线程可能会挂起 App。

    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')", @"北京市东城区南锣鼓巷纳福胡同xx号"];
    [_webview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@", result, error);
    }];

    Note: 方法不会阻塞线程,而且它的回调代码块总是在主线程中运行。

    使用JavaScriptCore库来做JS交互

    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    NSString *textJS = @"showAlert('这里是JS中alert弹出的message')";
    [context evaluateScript:textJS];

    JSValue 实例是对 JavaScript 值的引用。 您可以使用 JSValue 类来转换 JavaScript 和 Objective-C 或 Swift 之间的基本值(如数字和字符串),以便在本机代码和 JavaScript 代码之间传递数据。

    stringByEvaluatingJavaScriptFromString是一个同步的方法,使用它执行JS方法时,如果JS 方法比较耗的时候,会造成界面卡顿。尤其是js 弹出alert 的时候。

    alert 也会阻塞界面,等待用户响应,而stringByEvaluatingJavaScriptFromString又会等待js执行完毕返回。这就造成了死锁。

    官方推荐使用WKWebView的evaluateJavaScript:completionHandler:代替这个方法。

    其实我们也有另外一种方式,自定义一个延迟执行alert 的方法来防止阻塞,然后我们调用自定义的alert 方法。同理,耗时较长的js 方法也可以放到setTimeout 中。

    MessageHandler 是继 Native 截获 JS 假请求后另一种 JS 调用 Native 的方法,该方法利用了 WKWebView 的新特性实现。对比截获假 Request 的方法来说,MessageHandler 传参数更加简单方便。

    MessageHandler 

    WKUserContentController 类有一个方法:

    - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

    该方法用来添加一个脚本处理器,可以在处理器内对 JS 脚本调用的方法做出处理,从而达到 JS 调用 Native 的目的。

    参考文章:

    https://www.cnblogs.com/aibabel/p/11101759.html

    https://zhuanlan.zhihu.com/p/33092431


    转载本站文章《JavaScript与原生APP(Android/iOS)之间相互调用通信总结》,
    请注明出处:https://www.zhoulujun.cn/html/OS/IOS/IOS-Develop/2020_0710_8510.html

    下一篇:Last page