美文网首页iOS-OC中级
JavaScriptCore与iOS和Android交互

JavaScriptCore与iOS和Android交互

作者: iStig | 来源:发表于2016-02-25 11:40 被阅读3031次

    简介

    • 本文主要解决的是iOS和Android与JS交互,提供统一解决方案.
    • 关注点是以最简洁的代码和逻辑来解决三端的交互.
    • 讨论偏重iOS与JS交互 但会涉及到部分Android代码.

    iOS与JS交互的解决方案(本文讨论JavaScriptCore)

    关于JavaScriptCore

    • JSContext 代表JS的执行环境,通过-evaluateScript:方法就可以执行JS代码
    • JSValue 封装了JS与ObjC中的对应的类型,以及调用JS的API等
    • JSExport 是一个协议,遵守此协议,就可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,才能调用

    iOS调用JS代码并传参数

      // 一个JSContext对象,就类似于JS中的Window,只需要创建一次即可
      self.jsContext = [[JSContext alloc] init];
      
      // jscontext可以直接执行JS代码
      [self.jsContext evaluateScript:@"var num = 10"];
      [self.jsContext evaluateScript:@"var names = ['Same','Jack','Bob']"];
      [self.jsContext evaluateScript:@"var squareFunc = function(value) { return value * 2 }"];
      
      // 计算正方形的面积方法
      JSValue *square = [self.jsContext evaluateScript:@"squareFunc(num)"];
      
      // 通过下标的方式获取到方法
      JSValue *squareFunc = self.jsContext[@"squareFunc"];
      
      // 获取数组中第一个参数
      JSValue *names = self.jsContext[@"names"];
      JSValue *initialName = names[0];
      
      //JSValue包装了一个JS函数,我们可以从OC代码中使用Foundation类型作为参数来直接调用该函数
      JSValue *value = [squareFunc callWithArguments:@[@"20"]];
      
      // 输出JS调用结果
      NSLog(@"%@", square.toNumber);
      NSLog(@"%d", value.toInt32);
      NSLog(@"%@", initialName.toString);
    

    JS调用iOS (Blocks)

      // 获取当前JS运行环境
      self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
      
      // 将block被赋给JSContext里的一个标识符,JavaScriptCore 会自动的把block封装在JavaScript函数里。这使得在JavaScript中可以简单的使用Foundation和Cocoa类,本例中将韩语参数传入通过CFStringTransform()转义
      self.jsContext[@"simplifyString"] = ^(NSString *input) {
        NSMutableString *mutableString = [input mutableCopy];
        CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO);
        CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO);
        return mutableString;
      };
      
      //调用JS代码并且返回参数
      NSLog(@"%@", [self.jsContext evaluateScript:@"simplifyString('안녕하새요!')"]);
    

    JS调用iOS (JSExport)

    声明实现JSExport协议的协议TestJSObjectProtocol 并声明实现TestJSObjectProtocol协议的类JSNativeMethod
    //首先创建一个实现了JSExport协议的协议
    @protocol TestJSObjectProtocol <JSExport>
    
    //此处我们测试几种参数的情况
    - (NSString *)imgCallBack:(NSString *)url;
    
    // 通过JSON传过来
    - (void)callWithDict:(NSDictionary *)params;
    
    // JS调用此方法来调用OC的系统相册方法
    - (void)callSystemCamera;
    
    // JS调用Oc,然后在OC中通过调用JS方法来传值给JS。
    - (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params;
    
    // 在JS中调用时,函数名应该为showAlertMsg(arg1, arg2)
    // 这里是只两个参数的。
    - (void)showAlert:(NSString *)title msg:(NSString *)msg;
    @end
    
    @interface JSNativeMethod : NSObject<TestJSObjectProtocol>
    @property (nonatomic, weak) JSContext *jsContext;
    @end
    
    实现遵循JSExport协议的协议方法
    #import "JSNativeMethod.h"
    
    @implementation JSNativeMethod
    
    - (NSString *)imgCallBack:(NSString *)url {
      NSLog(@"touch image %@",url);
      return @"iOS To H5";
    }
    
    - (void)callWithDict:(NSDictionary *)params {
      NSLog(@"Js调用了OC的方法,参数为:%@", params);
    }
    
    // Js调用了callSystemCamera
    - (void)callSystemCamera {
      NSLog(@"JS调用了OC的方法,调起系统相册");
      
      // JS调用后OC后,又通过OC调用JS,但是这个是没有传参数的
      JSValue *jsFunc = self.jsContext[@"jsFunc"];
      [jsFunc callWithArguments:nil];
    }
    
    - (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {
      NSLog(@"jsCallObjcAndObjcCallJsWithDict was called, params is %@", params);
      
      // 调用JS的方法
      JSValue *jsParamFunc = self.jsContext[@"jsParamFunc"];
      [jsParamFunc callWithArguments:@[@{@"age": @10, @"name": @"lili", @"height": @158}]];
    }
    
    - (void)showAlert:(NSString *)title msg:(NSString *)msg {
      dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertView *a = [[UIAlertView alloc] initWithTitle:title message:msg delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [a show];
      });
    }
    @end
    
    把JSNativeMethod封装到JavaScript函数AndroidCall()中
     JSNativeMethod *call = [[JSNativeMethod alloc] init];
      //将JSNativeMethod封装到JavaScript函数AndroidCall()中
      self.jsContext[@"AndroidCall"] = call;
      call.jsContext = self.jsContext;
    

    异常处理

      // JSContext 还有另外一个有用的招数:通过设置上下文的 exceptionHandler 属性,你可以观察和记录语法,类型以及运行时错误。 exceptionHandler 是一个接收一个 JSContext 引用和异常本身的回调处理
      self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息:%@", exceptionValue);
      };
    

    Android对应实现

    protected void addJsMethod() {
    wv_content.addJavascriptInterface(new JSNativeMethod(), "AndroidCall");
    }
    
    final class JSNativeMethod {
    @JavascriptInterface
    public String imgCallBack(String url) {
    Toast.makeText(mContext, url, Toast.LENGTH_SHORT).show();
    return "test android";
    }
    }
    public void test(View view) {
    wv_content.loadUrl("javascript:test()");
    }
    

    JavaScript对应实现

    //点击图片事件
    <div id='buttons'></div> <div id='log'></div>
        <div>
            ![](http:https://img.haomeiwen.com/i138651/a619b898f4b69371.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
        </div>
    
    //点击按钮事件
     <input type="button" value="Call ObjC system camera" onclick="callSystemCamera()">
     <input type="button" value="Call ObjC system alert" onclick="showAlertMsg('js title', 'js message')">
     <input type="button" value="Call ObjC func with JSON " onclick="callWithDict({'name': 'testname', 'age': 10, 'height': 170})">
     <input type="button" value="Call ObjC func with JSON and ObjC call js func to pass args." onclick="jsCallObjcAndObjcCallJsWithDict({'name': 'testname1', 'age': 101, 'height': 180})">
    
    //app端callback callSystemCamera()
    function callSystemCamera() {
      log("callSystemCamera()");
      window.AndroidCall.callSystemCamera();
    }
    //app端callback showAlertMsg()
    function showAlertMsg(a,b) {
          log(JSON.stringify(a));
          log(JSON.stringify(b));
          window.AndroidCall.showAlertMsg(a,b);
    }
    //app端callback callWithDict()
    function callWithDict(a) {
          log(JSON.stringify(a));
          window.AndroidCall.callWithDict(a);
    }
    //app端callback jsCallObjcAndObjcCallJsWithDict()
    function jsCallObjcAndObjcCallJsWithDict(a) {
          log(JSON.stringify(a));
          window.AndroidCall.jsCallObjcAndObjcCallJsWithDict(a);
    }
    //点击图片事件
    function imgCallBack(src) {
            log("click img");
            log(JSON.stringify(src));
            if (window.AndroidCall) {
                log("called>>>>>");
     log(window.AndroidCall.imgCallBack('http://huizuche.com/Content/Images/recommend/car/Jeep_Grand%20Cherokee_L.jpg'));
            }
            else {
                alert("window.AndroidCall is nil");
            }
        }
    

    源代码

    https://github.com/iStig/JavaScriptCoreDemo

    扩展阅读

    NSHipster
    OC JavaScriptCore与js交互
    Android

    相关文章

      网友评论

      • a31b485b96d3:self.jsContext[@"simplifyString"] ,这种写法遇上重定向就无法响应了,该怎么处理重定向

      本文标题:JavaScriptCore与iOS和Android交互

      本文链接:https://www.haomeiwen.com/subject/jgbikttx.html