iOS Native与JS通信:JSBridge

news2024/9/20 22:39:43

文章目录

  • 一、简介
  • 二、JS 调用 Native
    • 1.使用 URL Scheme
      • a.UIWebView
      • b.WKWebView
    • 2.使用 JavaScriptCore (iOS 7+)
    • 3.使用 WKWebView 和 WKScriptMessageHandler (iOS 8+)
  • 三、Native 调用 JS
    • 1.使用 UIWebView
    • 2.使用 WKWebView
    • 3.使用 JavaScriptCore (iOS 7+)


一、简介

对于移动应用程序的开发,有多种技术选型,最基础的是原生开发,后面由于动态化和跨平台的需求,引入了跨端的方案,比如H5、RN。以原生+H5 混合开发模式为例,H5页面经常需要使用到Native端的功能,比如打开二维码扫描、调用本地相册、获取用户信息等,同时Native端也需要向H5页面发送消息、更新状态等。所以需要一种通信机制,来让两端进行通信。这时候就引入了桥(Bridge)的概念。

JSBridge(JavaScript Bridge)是一种设计模式,用于在JavaScript和原生代码(如iOS的Objective-C/Swift或Android的Java/Kotlin)之间建立通信桥梁。通过JSBridge,Web页面中的JavaScript代码可以调用原生功能,原生代码也可以调用JavaScript方法,从而实现Web和原生代码的互操作性。

JSBridge的核心思想是通过特定的通信协议在JavaScript和原生代码之间传递消息和数据(🔗iOS与JS交互)。其工作原理通常包括以下步骤:

  1. JavaScript调用原生代码:
  • JavaScript通过调用特定的接口或注入的对象,发送消息给原生代码。
  • 原生代码接收到消息后,执行相应的操作,并将结果返回给JavaScript。
  1. 原生代码调用JavaScript:
  • 原生代码通过特定的接口或注入的对象,发送消息给JavaScript。
  • JavaScript接收到消息后,执行相应的操作,并将结果返回给原生代码。

二、JS 调用 Native

1.使用 URL Scheme

这种方法通过在 JavaScript 端构建一个特定的 URL,然后在原生端捕获这个 URL 并解析出需要调用的方法和参数。

a.UIWebView

使用 UIWebView 的代理方法 webView:shouldStartLoadWithRequest:navigationType: 来拦截即将加载的请求,并根据请求的URL参数执行相应的逻辑,包括注入JavaScript代码、显示提示信息、处理登录结果等。

JS端:

function callNativeMethod(method, params) {
    var url = "myapp://" + method + "?" + encodeURIComponent(JSON.stringify(params));
    window.location.href = url;
}

Native端:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];
    if ([[url scheme] isEqualToString:@"myapp"]) {
        NSString *method = [url host];
        NSString *query = [url query];
        NSData *data = [query dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *params = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        
        if ([method isEqualToString:@"someNativeMethod"]) {
            [self someNativeMethod:params];
        }
        return NO;
    }
    return YES;

这里举一个例子:

stringByEvaluatingJavaScriptFromString: 方法返回执行JS脚本的结果,通过这种方式,我们可以在UIWebView中拦截即将加载的请求,注入JavaScript代码,并根据URL参数执行相应的逻辑。这种方法可以用于处理各种需要与Web内容交互的场景,如动态调整页面布局、显示提示信息、处理登录结果等。(UIWebView在iOS 12之后已经被废弃)
在这里插入图片描述

b.WKWebView

WKWebView 有两个代理,一个是 WKNavigationDelegate,另一个是 WKUIDelegate。这里是使用WKWebView的代理方法 webView:decidePolicyForNavigationAction:decisionHandler:

JS端:

function callNative() {
    loadURL("your_func_name://xxx");
}  

Native端:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL *url = navigationAction.request.URL;
    // 与约定好的函数名作比较
    if ([[url scheme] isEqualToString:@"your_func_name"]) {
        // just do it
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

//decisionHandler 是当你的应用程序决定是允许还是取消导航时,要调用的代码块。 
//该代码块使用单个参数,它必须是枚举类型 WKNavigationActionPolicy 的常量之一。如果不调用 decisionHandler 会引起 crash。

这里简单介绍一下 WKUIDelegate 中的代理方法 webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:

webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler: 是 WKUIDelegate 协议中的一个方法。当网页中通过 JavaScript 调用 alert 方法时,WebKit 会调用这个委托方法。你可以在此方法中自定义显示一个警告框并在用户点击确定按钮后执行相应的操作。也就是说,可以将 JS 端调用 alert 方法视作向 Native 发送一个消息,Native 接受到这个消息后实现一些自定义的行为,但这种行为一般都是对警告框进行操作。

2.使用 JavaScriptCore (iOS 7+)

iOS 7 有了 JavaScriptCore 专门用来做 Native 与 JS 的交互。我们可以在 webview 完成加载之后获取 JSContext,然后利用 JSContext 将 JS 中的对象引用过来用 Native 代码对其作出解释或响应。先贴个文档🔗深入理解JSCore

JS端:

function callNative() {
		native.showAlert('Hello from JavaScript!');
}

Native端:

#import <UIKit/UIKit.h>
#import <JavaScriptCore/JavaScriptCore.h>

// 定义一个协议,声明可以被 JavaScript 调用的方法
@protocol JSExportProtocol <JSExport>
- (void)showAlert:(NSString *)message;
@end

@interface ViewController : UIViewController <JSExportProtocol>

@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) JSContext *jsContext;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化并配置 UIWebView
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:self.webView];    
    
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 获取 JavaScript 上下文
    
    self.jsContext[@"native"] = self;  // 绑定原生对象到 JavaScript 上下文
}

// 实现协议方法,供 JavaScript 调用
- (void)showAlert:(NSString *)message {/*......*/}
@end

3.使用 WKWebView 和 WKScriptMessageHandler (iOS 8+)

当应用程序需要一种方法来响应网页视图中的JavaScript消息时,请采用WKScriptMessageHandler协议。当JavaScript代码发送一个特定目标的消息到消息处理器时,WebKit将调用处理器的userContentController:didReceiveScriptMessage:方法。使用该方法来实现响应。例如,可以根据网页内容的更改来更新应用程序的其他部分。

@protocol WKScriptMessageHandler

如何添加一个消息处理器呢?WKUserContentController 类有一个方法:

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
//scriptMessageHandler:实现了 WKScriptMessageHandler 协议的对象,该对象会处理来自 JavaScript 的消息。
//name:消息的名称,JavaScript 通过这个名称发送消息。
//The name of this function is window.webkit.messageHandlers.<name>.postMessage(<messageBody>),
//where <name> corresponds to the value of this parameter. For example, if you specify the string MyFunction, 
//the user content controller defines the window.webkit.messageHandlers.MyFunction.postMessage() function in JavaScript.

举个例子:

@interface ViewController () <WKScriptMessageHandler>
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // 配置 WebView
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    WKUserContentController *userContentController = [[WKUserContentController alloc] init];
    [userContentController addScriptMessageHandler:self name:@"messageHandler"]; //----------添加脚本处理器-----------------
    configuration.userContentController = userContentController;
    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:webView];
}

//WKScriptMessageHandler 协议方法,在接收到脚本信息时触发 //------------我们需要实现它,处理来自 JavaScript 的消息-------------------
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"messageHandler"]) {
        // ......
        NSLog(@"Received message from JavaScript: %@", message.body);
    }
}
@end

//在JS中按下面方式调用就可以了
window.webkit.messageHandlers.messageHandler.postMessage({body: 'Hello, world!'});

这里解释一下 WKUserContentController 和 WKWebView 的关系:

在 WKWebView 的初始化函数中有一个入参 configuration,它的类型是 WKWebViewConfiguration。WKWebViewConfiguration 中包含一个属性 userContentController,这个 userContentController 就是 WKUserContentController 类型的实例,我们可以用这个 userContentController 来添加不同名称的脚本处理器。

@interface WKWebView : UIView
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
@end

@interface WKWebViewConfiguration : NSObject <NSSecureCoding, NSCopying>
@property (nonatomic, strong) WKUserContentController *userContentController;
@end

这里需要注意一下循环引用的问题:scriptMessageHandler 入参会被强引用,那么如果你把当前 WKWebView 所在的 UIViewController 作为第一个入参,这个 viewController 被他自己所持有的 webview.configuration. userContentController 所持有,就会造成循环引用。

在这里插入图片描述

所以一般情况下我们的代码会在 viewWillAppear 和 viewWillDisappear 成对儿的添加和删除 MessageHandler:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.webview.configuration.userContentController addScriptMessageHandler:self name:@"YourFuncName"];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.webview.configuration.userContentController removeScriptMessageHandlerForName:@"YourFuncName"];
}

三、Native 调用 JS

1.使用 UIWebView

在 UIWebView 中,可以使用 stringByEvaluatingJavaScriptFromString: 方法,来返回运行JavaScript脚本的结果。它是一个同步方法,会阻塞当前线程!

在这里插入图片描述

2.使用 WKWebView

在 WKWebView 中,可以使用 evaluateJavaScript:completionHandler: 方法来调用 JavaScript。因为它异步执行 JavaScript 代码,所以不会阻塞主线程,并在完成后通过回调处理结果,它的回调代码块总是在主线程中运行。

- (void)evaluateJavaScript:(NSString *)javaScriptString 
         completionHandler:(void (^)(id, NSError *error))completionHandler;

3.使用 JavaScriptCore (iOS 7+)

JavaScriptCore 框架允许直接在 Objective-C 和 JavaScript 之间互相调用方法。

Native端:

#import <JavaScriptCore/JavaScriptCore.h>

@interface ViewController ()
@property (nonatomic, strong) JSContext *jsContext;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
}

- (void)callJavaScriptFunction {
    JSValue *jsFunction = self.jsContext[@"javascriptFunctionName"];
    [jsFunction callWithArguments:@[@"param1", @"param2"]];
}
@end

JS端:

function javascriptFunctionName(param1, param2) {
    console.log("Called from Native with params:", param1, param2);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2058834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江西学术会议:第五届计算机、大数据与人工智能国际会议

第五届计算机、大数据与人工智能国际会议(ICCBDAI 2024)将于2024年11月1日-3日在江西景德镇召开。本届会议由景德镇陶瓷大学主办&#xff0c;西安交通大学、暨南大学、南京邮电大学、景德镇学院、ELSP&#xff08;爱迩思出版社&#xff09;、ESBK国际学术交流中心、AC学术平台协…

ctfshow之web29~web51

目录 web29 题解&#xff1a; web30 web31 web32&#xff08;32~36&#xff09; web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 &#xff08;42~51&#xff09; web43 web44 web45 web50 web51 web29 前瞻知识&#xff1a; isset() …

【sgCreateReadonlyForm】自定义小工具:敏捷开发→自动化生成只读表单代码片段脚本(无需列表展示数据,多用于查看某一条数据记录)

sgCreateReadonlyForm源码 <template><!-- 前往https://blog.csdn.net/qq_37860634/article/details/141389231 查看使用说明 --><div :class"$options.name"><div class"sg-head">只读表单生成工具<el-dropdown:show-timeo…

8.20 roles的基本用法+使用剧本安装nginx

安装nginx并更改其端口 创建目录 mkdir /etc/ansible/playbook 编辑配置文件 vim /etc/ansible/palybook/nginx.yml --- - hosts: s remote_user: root tasks: - name: 卸载httpd yu…

人工智能 | 结对编程助手GithubCopilot

简介 GitHub Copilot 是一款 AI 结对程序员&#xff0c;可帮助您更快、更少地编写代码。它从注释和代码中提取上下文&#xff0c;以立即建议单独的行和整个函数。GitHub Copilot 由 GitHub、OpenAI 和 Microsoft 开发的生成式 AI 模型提供支持。它可作为 Visual Studio Code、…

智慧水务平台:数智化驱动,‌实现管理全面升级!‌

智慧生产体系聚焦水务行业的生产环节,涵盖水源管理、水厂管理、生产调度、二次供水管理等各个环节。对各生产环节的实时生产数据和设备运行参数进行监测,并提供报警、日常运维、能耗分析、流程优化,为水务生产管理的成本压降、效率提升、安全保障、服务优化提供支撑。 智慧管网…

Echarts添加水印

如果直接说水印,很难在官方找到一些痕迹,但是换个词【纹理】就能找到了。水印就是一种特殊的纹理背景。 Echarts-backgroundColor backgroundColor 支持使用rgb(255,255,255),rgba(255,255,255,1),#fff等方式设置为纯色,也支持设置为渐变色和纹理填充,具体见option.colo…

哪个牌子的开放式耳机性价比高?五款地表最强机型推荐!

在我们的日常生活中&#xff0c;街道、地铁车厢或公交车等地方常常充满了噪音&#xff0c;这些杂音不仅可能扰乱心情&#xff0c;还可能对我们的听力造成潜在的伤害。在这样的环境下&#xff0c;如果想要享受音乐或追剧&#xff0c;同时又能保持对周围环境的警觉&#xff0c;开…

充电宝哪个品牌好?360度全方面测评热门款充电宝

在这个智能手机、平板电脑等移动设备普及的时代&#xff0c;充电宝已成为我们日常生活中不可或缺的伴侣。无论是在通勤途中、旅行出行&#xff0c;还是在户外运动时&#xff0c;充电宝都能为我们的设备提供源源不断的电力支持。然而&#xff0c;市场上充电宝品牌众多&#xff0…

c++开发,下载安装Boost库并检测是否安装成功

c开发&#xff0c;下载安装Boost库并检测是否安装成功 系统说明下载Boost库安装测试验证 系统说明 win10系统 下载Boost库 从官方网站下载&#xff0c;点击版本号 进去后选择windows系统的下载 安装 第1步 将下载后的压缩包解压到你想存储的文件夹中&#xff0c;比如我这里…

自主身份:Web3如何重新定义个人数据所有权

随着数字时代的快速发展&#xff0c;个人数据成为了一种新型的资产&#xff0c;深刻影响着我们的生活。然而&#xff0c;在Web2时代&#xff0c;个人数据往往被科技巨头所掌控&#xff0c;用户在享受互联网服务时&#xff0c;无意中失去了对自己数据的控制权。Web3的到来&#…

Java 调整字符串,验证码生成

package text7;public class ZiFanz {public static void main(String[] args) {//1.定义两个字符串String strA "abcde";String strB "deabc";//2.abcde->bcdea->cdeab->deabc旋转字符串//旋转并比较boolean result cheak(strA, strB);System…

时间序列分析中的特征提取

一、说明 在多变量时间序列分析期间&#xff0c;数据包含随时间推移测量的多个数据。为了管理模型性能&#xff0c;建议进行特征提取&#xff0c;以使模型的数据点更加紧凑。 二、时间序列的挑战 2.1 特征提取 仅选择“信息性”特征&#xff0c;这些特征在多变量时间序列分析…

【Java】了解线程 Thread 类的使用,如何创建、终止、等待一个线程,一文读懂不迷路

线程是什么 线程是操作系统中调度的基本单位&#xff0c;是比进程更小的执行单元。线程在进程内部运行&#xff0c;共享该进程的资源&#xff0c;如内存和文件句柄&#xff0c;但每个线程都有自己的执行栈和程序计数器。 线程的主要特点包括&#xff1a; 轻量级&#xff1a;…

格式工厂怎么转换mp4?简单4步实现视频转换

视频转换的重要性不言而喻。随着科技的发展&#xff0c;视频已经成为我们生活中不可或缺的一部分。然而&#xff0c;不同的设备和平台往往支持不同的视频格式&#xff0c;这就导致了视频兼容性问题。此外&#xff0c;不同格式的视频文件在存储和传输方面也存在差异。因此&#…

【MySQL】数据库基础与MySQL的安装

1. 数据库基础 1.1 什么是数据库 在接触数据库之前&#xff0c;回想一下我们之前写的所有小项目&#xff0c;如果需要持久化保存一些内容&#xff0c;我们是保存在文件中的&#xff0c;似乎也能够很不错的支持我们的操作&#xff0c;解决我们的需求。但是&#xff0c;实际上是…

健身房管理系统的设计与实现设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

Redis7基础篇(七)

redis哨兵&#xff08;sentinel&#xff09; 目录 redis哨兵&#xff08;sentinel&#xff09; 是什么 能干吗 案例演示 架构 案例步骤 出现的问题 哨兵的运行流程和选举原理 哨兵的使用建议​编辑 是什么 在之前的复制中我们了解到 主机shutdown之后 从机就会一直等…

轻松创作高质量的AI音乐——Suno API

Suno 歌曲生成 API 对接指南 随着人工智能技术的飞速发展&#xff0c;各类 AI 程序已如雨后春笋般涌现。AI 不再是遥不可及的存在&#xff0c;它的身影深入了人类工作与生活的每一个角落。其应用领域也愈加广泛&#xff0c;从初期的写作&#xff0c;到现如今的医疗、教育&…

为什么同一台手机连着电脑的ip地址不一样

在现代社会中&#xff0c;网络已成为我们日常生活不可或缺的一部分。从日常办公到休闲娱乐&#xff0c;网络无处不在。然而&#xff0c;在享受网络带来的便利时&#xff0c;我们可能会遇到一些看似复杂实则有趣的网络现象。今天&#xff0c;我们就来探讨一个常见却又容易被忽视…