简化Cocos和Native交互利器_native.reflection.callstaticmethod-程序员宅基地

技术标签: 注解  游戏引擎  cocos2d  Cocos  

背景

我们在使用 Cocos 和 Native 进行交互的时候,发现体验并不是特别的友好。
如下所示,为我们项目当中的一段代码(代码已脱敏),当检测到发生了 js 异常,我们需要通知 Native 端去做一些处理。

jsException: function (scence, msg, stack) {
    if (cc.sys.isNative && cc.sys.os === cc.sys.OS_ANDROID) {
        jsb.reflection.callStaticMethod(
            "xxx/xxx/xxx/xxx/xxx",
            "xxx",
            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
            scence, msg, stack
        );
    } else if (cc.sys.isNative && cc.sys.os === cc.sys.OS_IOS) {
        jsb.reflection.callStaticMethod(
            "xxx",
            "xxx:xxx:xxx:",
            scence, msg, stack
        );
    }
}

从代码当中我们可以看出有以下问题:

  1. 方法调用非常麻烦,特别是 Android 端,需要明确的指出方法的路径,方法名,参数类型,特别容易出错;
  2. 一旦 Native 端的方法发生变化,Cocos层必须要同步修改,否则就会出现异常,甚至有可能 crash;
  3. 不方便回调;

我们尝试着用注解的思路来解决这些问题,从而设计并实现了 ABCBinding 。ABCBinding 能够极大的简化 Cocos和 Native 的交互,方便维护。

ABCBinding 的结构设计

ABCBinding 的结构如下图所示:
在这里插入图片描述

ABCBinding 包含 Native 和 TS 两个部分,Native 负责约束本地的方法,TS 负责对 Cocos 层提供调用 Native 方法的接口。
我们重新定义了一套 Cocos 和 Native 的交互模式:

  1. 提供 Cocos 访问的 Native 方法必须为 public static,且参数必须为 Transfer(Transfer 为 SDK 提供的接口,能够在 Cocos 层和 Native 层传递数据);
  2. 方法必须使用 ABCBinder 注解修饰,并在注解内传入约定好的 tag,此 tag 用于唯一标识这个方法;
  3. 使用 SDK 提供的 callNativeMethod 方法,传入约定好的 tag 调用 Native 方法。

例子:
如下所示,为调用 Native 方法去下载,我们只需要传入约定好的 tag:downloadFile,并传入参数,便可以进行下载了。
TS 层:

Binding.callNativeMethod('downloadFile', { url: 'https://xxx.jpeg' }
).then((result) => {
    this.label.string = `下载成功:path=${result.path}`;
}).catch((error) => {
    this.label.string = error.msg;
});

Native 层:

@ABCBinder("downloadFile")
public static void download(Transfer transfer){
    new Thread(new Runnable() {
        @Override
        public void run() {
            String url = transfer.get("url","");
            try{
                //下载中
	        ...
                //下载成功
                TransferData data = new TransferData();
                data.put("path","/xxx/xxx.jpg");
                transfer.onSuccess(data);
            }catch (Exception e){
                //失败
                transfer.onFailure(e);
            }
        }
    }).start();
}

通过例子可以看到,使用 ABCBinding 能够让 Cocos 和 Native 的交互简单很多,我们再也不用再传入复杂的路径和参数了,而且回调也变得很优雅。接下来我们来看看 ABCBinding 是如何实现的。

具体实现

从上面的例子我们可以看出,ABCBinding 是通过 tag 来实现 Cocos 和 Native 进行交互的,那 SDK 是如何通过 tag 来找到对应的方法呢?

通过 tag 找到 Native 方法

我们定义了编译时注解 ABCBinder,

@Retention(RetentionPolicy.CLASS)//编译时生效
@Target({ElementType.METHOD})//描述方法
public @interface ABCBinder {
    String value();
}

在编译期间会生成一个类 ABCBindingProxy,成员变量 executors 包含了所有对应的 tag 和方法。其中真正可执行的方法被包装在了 ExecutableMethod 接口当中。

//以下为自动生成的代码
private Map executors = new java.util.HashMap();

private ABCBindingProxy() {
  executors.put("test2",new ExecutableMethod() {
    @Override
    public void execute(Transfer t) {
      com.example.abcbinding.MainActivity.test2(t);
    }
  });
  executors.put("test1",new ExecutableMethod() {
    @Override
    public void execute(Transfer t) {
      com.example.abcbinding.MainActivity.test1(t);
    }
  });
}
public interface ExecutableMethod {
    void execute(Transfer t);
}

因此我们只需要通过 executors 和 tag 就可以找到了对应的方法了,接着我们看看 TS 是如何与 Native 进行交互的,
以下是 SDK 里面 TS 层的部分代码,SDK 屏蔽了调用的具体细节,将请求的参数转变成为 json 字符串,并将相关的参数传递给 SDK 内部的方法 execute,由 execute 将请求转发给真正的方法。

public callNativeMethod(methodName: string, args ?: Record<string, string | number | boolean>): Promise < any > {
    ...
    if (cc.sys.isNative && cc.sys.os === cc.sys.OS_ANDROID) {
        let resultCode = jsb.reflection.callStaticMethod(
            'com/tencent/abckit/binding/ABCBinding', 
            'execute', 
            '(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I',
            methodName, 
            typeof args === 'object' ? JSON.stringify(args) : '', cbName);
        ...
    } else if (cc.sys.isNative && cc.sys.os === cc.sys.OS_IOS) {
        let retId = jsb.reflection.callStaticMethod(
            'ABCBinding', 
            'execute:methodName:args:callback:',
            methodName, 
            typeof args === 'object' ? JSON.stringify(args) : '', cbName);
        ...
    } else {
        let msg = 'no implemented on this platform';
        reject(msg);
    }
}

这样,我们就解决了通过 tag 找到对应方法的问题。但还有两个问题需要解决:
如何约束 Native 方法?以及如何保证 tag 的唯一性?

约束 Native 方法

ABCBinding 规定,提供 Cocos 访问的 Native 方法必须为 public static,参数必须为 Transfer,且 tag 必须要保持唯一性,那怎么来约束呢?
在代码的编译期间,我们会去检查所有被 ABCBinder 修饰的方法,若发现这个方法并不符合我们的规范,则会直接报错。
如下所示:
1.参数错误

@ABCBinder("test1")
public static void test1(Transfer transfer, int a) {
    Log.i("测试", "test()");
}

在这里插入图片描述

2.方法非 static

@ABCBinder("test2")
public void test2(Transfer transfer) {
    Log.i("测试", "test()");
}

在这里插入图片描述

3.方法非 public

@ABCBinder("test3")
protected static void test3(Transfer transfer) {
    Log.i("测试", "test()");
}

在这里插入图片描述

4.tag 重复

@ABCBinder("helloworld")
public static void test1(Transfer transfer) {
    Log.i("测试", "test()");
}

@ABCBinder("helloworld")
public static void test2(Transfer transfer) {
    Log.i("测试", "test()");
}

在这里插入图片描述

优雅的回调

SDK 会在编译期间自动生成 callJs 方法,所有的回调都是通过 callJs 方法实现。Native 方法只需要调用 Transfer 所提供的回调接口,便可以轻松的将结果回调给 Cocos。由于 callJs 代码是自动生成,所以 SDK 不需要直接依赖 Cocos 库,只需要业务方依赖即可。

//以下为自动生成的代码
public void callJs(final String globalMethod, final TransferData params) {
  org.cocos2dx.lib.Cocos2dxHelper.runOnGLThread(new Runnable() {
    @Override
    public void run() {
      String command = String.format("window && window.%s && window.%s(%s);", 
	  globalMethod,globalMethod, params.parseJson());;
      org.cocos2dx.lib.Cocos2dxJavascriptJavaBridge.evalString(command);;
    }
  });
}

ABCBinding 提供了onProcess,onSuccess 和 onFailed 回调方法,
以下为 downloadFile 接口的的回调示例:
TS 层:

Binding.withOptions({
    //回调onProcess
    onProgress: (progress) => {
        let current = progress.current;
        this.label.string = `progress:${current}`;
    }
}).callNativeMethod(
    'downloadFile',
    { url: 'https://xxxx.jpg' }
).then((result) => {
    //回调onSuccess
    this.label.string = `下载成功:path=${result.path}`;
}).catch((error) => {
    //回调onFailed
    if (error) {
        this.label.string = error.msg;
    }
});

Native 层:

@ABCBinder("downloadFile")
public static void download(Transfer transfer){
    new Thread(new Runnable() {
        @Override
        public void run() {
            String url = transfer.get("url","");
            try{
              	//模拟下载过程
                for(int i =0;i<100;i++) {
                     transfer.onProgress("current", i);
                }
	       //下载成功
                TransferData data = new TransferData();
                data.put("path","/xxx/xxx.jpg");
                transfer.onSuccess(data);
            }catch (Exception e){
                //失败
                transfer.onFailure(e);
            }
        }
    }).start();
}

其他 feature

抹平系统差异

使用 ABCBinding 无须再判断当前系统是 Android 还是 IOS ,只需对应 Native 方法的 tag 保持一致即可。

Binding.callNativeMethod('isLowDevice').then(({isLowDevice}) => {
  console.log(isLowDevice);
})

无需关心线程切换

Native 方法使用 Transfer 回调时,ABCBinding 会自动切换到 Cocos 线程执行,无需业务方关心。

@ABCBinding("getHardwareInfo")
public static void getHardwareInfo(Transfer transfer) {
    TransferData data = new TransferData();
    data.put("brand", Build.BRAND);
    data.put("model", Build.MODEL);
    data.put("OsVersion", Build.VERSION.RELEASE);
    data.put("SdkVersion", Build.VERSION.SDK);
    transfer.onSuccess(data);
}

支持超时

ABCBinding 支持设置超时,其中超时的时间单位为秒,如下所示,超时会回调到 catch 方法当中。

Binding.withOptions({
    timeout: 120,
    onProgress: (progress) => {
        let current = progress.current;
        this.label.string = `progress:${current}`;
    }
}).callNativeMethod(
    'downloadFile',
    { url: 'https://xxxx.jpg' }
).then((result) => {
    this.label.string = `下载成功:path=${result.path}`;
}).catch((error) => {
    if (error.code == ERROR_CODE_TIMEOUT) {
         console.log("超时");
    }
});

彩蛋:在热更新当中的应用

我们在使用 Cocos 热更新服务的过程中发现,怎么确定 Cocos 热更新包能够发布到哪个 App 版本,是个难题。Cocos 热更新包能不能在这个 App 版本上正确运行,跟两个因素有关,Cocos 版本和 Native 接口。
Cocos 版本
如果 App 和热更包的 Cocos 版本不一致,那么很有可能这个热更包无法在 App 上运行,除非官方做了一些兼容处理。不过这个因素可控,Cocos 的版本不会频繁的升级,而且我们知道 Cocos 版本和 App 版本的对应关系。
Native 接口
如果热更包调用了某个 Native 的接口,但是这个接口在有些版本上不存在,那该热更包就无法在这个版本的 App 上运行。
在我们的业务场景当中,Cocos 版本不会频繁变更,但是每个版本的 Native 代码可能会相差较大,人工来核对每个版本的 Native 接口变更是一件极为费时费力的事情。
那么 ABCBinding 能帮助我们做什么呢?

让热更包兼容所有版本的 App

首先我们去除 Cocos 版本的因素,因为这个因素可控,且业务方无法解决。
ABCBinding 知道本地有哪些接口可用,所以当 Cocos 调用了一个不存在的接口时,我们会返回一个特殊的 code,这样热更包只需要在内部做兼容处理就可以了。
如下所示:

Binding.callNativeMethod('downloadFile', { url: 'https://xxx.jpeg' }
).then((result) => {
     //处理逻辑
}).catch((error) => {
    if(error.code == ERROR_CODE_METHOD_NOT_DEFINED){
        console.log("未找到该方法");
    }
});

元素绑定

既然热更新跟这两个元素有关,我们就可以通过这两个元素,让 App 和热更包进行匹配,如果能够匹配,那么这个热更包就可以下发到这个版本的 App 上。
Cocos 版本:我们在打包的时候就可以确定;
Native 接口:在打包的过程中,ABCBinding 可以将当前所支持的接口,按照约定的方式生成一个元素。
例如:本地的接口有 test1,test2,test3 我们可以将接口按照指定的方式排序拼接取 md5 值,生成第二个元素。
这样当 App 和热更新包的两个元素能够匹配时,就能够下发了。
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hbdatouerzi/article/details/121331093

智能推荐

R语言gganimate动态图_r语言做动态图-程序员宅基地

文章浏览阅读5.7k次,点赞2次,收藏21次。library(gapminder)library(dplyr)library(gganimate)gapminder %>% ggplot(aes(x = gdpPercap, y = lifeExp, size = pop, color = continent)) + geom_point() + scale_x_log10() + transition_m..._r语言做动态图

UE4 鼠标控制actor旋转_ue4鼠标控制物体旋转移动-程序员宅基地

文章浏览阅读6.5k次,点赞3次,收藏25次。物体Tick事件转动,初始不动则Rotate 都为0,关卡蓝图里边鼠标左键事件,鼠标点击和释放的位置做减法 并设置目标的Rotate属性,Clamp做速度控制.不知道其他有没有好的办法,项目需要没找到相关案例 临时写的..._ue4鼠标控制物体旋转移动

Android 应用开发基础总结_android开发-程序员宅基地

文章浏览阅读833次。Android Studio:Android Studio 是目前最流行的 Android 开发工具,掌握 Android Studio 的功能、配置、使用方法等,有助于提高开发效率、降低开发成本。Java 编程语言:Java 是 Android 应用开发的基础编程语言,需要掌握 Java 中的基础语法、数据类型、运算符、条件语句、循环语句、数组等基础知识。布局和控件:Android 应用的布局和控件是应用程序的基础,掌握 Android 布局文件、控件的种类、属性和样式等方面,是应用开发的必要基础。_android开发

2024年Android社招面试题,百度、阿里、滴滴、新浪的面试心经总结-程序员宅基地

文章浏览阅读984次,点赞30次,收藏14次。最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。还有高级架构技术进阶脑图、Android开发面试专题资料帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

C++在实时系统中的应用和优化策略是什么?-程序员宅基地

文章浏览阅读883次,点赞17次,收藏17次。然而,需要注意的是,优化并非一蹴而就的过程,需要开发者根据具体的应用场景和需求进行持续的探索和实践。这些引擎能够实时处理复杂的图形数据,实现高质量的渲染效果,为游戏、虚拟现实(VR)和增强现实(AR)等应用提供强大的支持。因此,对于C++开发者来说,保持对新技术的关注和学习,不断提升自己的技能水平,是应对未来挑战的关键。(3)内存对齐:合理的内存对齐可以提高数据的访问速度,减少CPU的寻址时间。实时系统对内存的使用有着严格的要求,因此内存管理优化是C++在实时系统中应用的关键环节。

类似于邮件管理,具有全选,反选功能的代码-程序员宅基地

文章浏览阅读325次。&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;head&gt;&lt;_3.2、类似如邮箱中对邮件的管理:选择邮件、删除邮件、全选、反选;如下图:小方框表

随便推点

python数组越界_python数组越界-程序员宅基地

文章浏览阅读1.8k次。Numpy入门标题中的英文首字母大写比较规范,但在python实际使用中均为小写。2018年7月23日笔记0. 学习内容:Python科学计算库:Numpy需要掌握的知识:1.Numpy简介;2.Numpy程序包;3.简单的Numpy程序;4.为什么使用Numpy;5.Numpy是什么;6.Numpy...文章潇洒坤2018-07-26943浏览量Python基础变量类型——List浅析Pytho..._pytorch数组越界

USB Audio Class (UAC) 分析_snd_usb_audio_create-程序员宅基地

文章浏览阅读2.5w次,点赞3次,收藏35次。本文记录USB Audio Class 驱动分析过程。_snd_usb_audio_create

冒泡排序总结_冒泡排序实验总结-程序员宅基地

文章浏览阅读2k次。冒泡排序总结算法思想:图片演示代码冒泡排序常规版:冒泡排序优化第一版冒泡排序优化第二版算法思想:冒泡排序是一种交换排序。交换排序就是通过元素按照要求进行两两比较,判断是否符合要求,假如不符合要求就将两个元素交换位置来达到排序的目的。冒泡排序名字的由来就是因为在交换过程中,类似水冒泡,小(大)的元素经过不断的比较交换由水底慢慢的浮到水的顶端。冒泡排序的思想就是利用的比较交换,利用循环将第 i 小或者大的元素归位,归位操作利用的是对 n 个元素中相邻的两个进行比较,如果顺序正确就不交换,如果顺序错误就_冒泡排序实验总结

iOS10 IDFA获取不了解决方案_idfa获取失败-程序员宅基地

文章浏览阅读6.3k次。苹果iOS10即将发布。ImportantIn iOS 10.0 and later, the value of advertisingIdentifier is all zeroes when the user has limited ad tracking.如果用户限制广告追踪,开发者获取IDFA将是 一串数字 0。这会极大的影响广告商以及需要推广APP的开发者_idfa获取失败

Android 不规则封闭区域填充 手指秒变油漆桶,手把手教你5G时代Webview的正确使用姿势-程序员宅基地

文章浏览阅读919次,点赞18次,收藏29次。学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)

数说CS|中国人民大学高瓴人工智能学院保研生源大起底!_中国人民大学计算机保研来源-程序员宅基地

文章浏览阅读3.1k次。1、院校介绍中国人民大学高瓴人工智能学院由高瓴资本创始人兼CEO、耶鲁大学校董、中国人民大学校友张磊先生捐资支持,是中国人民大学二级学院,于2019年成立。该学院主要招收人工智能专业的学生,对程序设计、数据结构与算法、计算机、人工智能专业综合能力以及英语听说读写能力有较高要求。人大高瓴不仅位于北京,而且还有着一流的师资力量,广受计算机保研er的欢迎,竞争较为激烈。2、培养特色※以上信息综合搜集整理自院系官网。如有信息偏误,欢迎留言评论指出学长学姐评价:评价一:人大高瓴人工智._中国人民大学计算机保研来源

推荐文章

热门文章

相关标签