0%

讲讲c++如何统一不同平台的接口吧

c++如何调用objectC

ios中 c++ 的文件可以用 .cpp 和 .mm 来结尾
区别是:
.mm :带有这种扩展名的源代码文件,除了可以包含objective-c和C代码以外还可以包含C++代码。
.cpp:只能编译C++

所以,如果要从c++ 中调用 ios 底层的内容, .mm 文件必不可少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
.h 文件
#ifndef Test_hpp
#define Test_hpp

#include"cocos2d.h"
#include <stdio.h>
class TestFun {
public:
TestFun();
~TestFun();
std::string getStrFromOC();
};
#endif /* Test_hpp */
.mm 文件

#include "Test.hpp"
#import "AppController.h"

TestFun::TestFun(){
cocos2d::log("TestFun create");
}
TestFun::~TestFun(){

}
// .mm 文件是可以直接调用 oc 中的方法的
std::string TestFun::getStrFromOC(){
return [[AppController myTestFun] UTF8String];
}

//AppController.mm 文件添加内容

+ (NSString *)myTestFun
{
return @"TestStr from AppController.h";
}

//调用部分在 AppDelegate

LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));

auto testClass = new TestFun();
std::string str = testClass->getStrFromOC();
cocos2d::log("fun call after is %s",str.c_str());

//打印 log

TestFun create
fun call after is TestStr from AppController.h
[LUA-print]
**********

由此可见 c++ 调用object-C 比较容易,更改后缀为.mm 就可以使用

c++如何调用java

既然是懒人,那就直接使用Test class 来做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
.h 文件
#ifndef Test_hpp
#define Test_hpp

#include"cocos2d.h"

class TestFun {

public:
TestFun();
~TestFun();
std::string getStrFromOC();
//添加获取bunleId 的函数
std::string getBundleID();
};
#endif /* Test_hpp */

.mm 文件
<!-- 添加新的函数 getBundleID-->
std::string TestFun::getBundleID(){
std::string bundleId = "";
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
<!-- 获取ios的上面已经写过了这里就不啰嗦了 -->
bundleId = "";
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
<!-- 添加宏 判断平台 -->
#include "platform/android/jni/JniHelper.h"
<!-- 引入头java native interface 需要使用的文件 -->
cocos2d::JniMethodInfo t; //jni 方法结构体 下面的使用需要这个结构体
if(cocos2d::JniHelper::getStaticMethodInfo(t, "org/cocos2dx/lua/AppActivity", "getBundleID", "()Ljava/lang/String;")){
<!-- getBundleID 传递参数为空 返回string 类型 -->
jstring bundle = (jstring) t.env->CallStaticObjectMethod(t.classID, t.methodID);
<!-- 需要把返回的jstring 转化为 string -->
bundleId = cocos2d::JniHelper::jstring2string(bundle);
}
#endif
return bundleId;
}


<!-- 查看了下jni 的写法 -->
typedef struct JniMethodInfo_
{
JNIEnv * env;
jclass classID;
jmethodID methodID;
} JniMethodInfo;
这个是jni 方法的结构体

getStaticMethodInfo 方法内容
1.判断是否传递参数
2.获取jni 环境
3.获取所要调用的class 的id
4.获取所需要调用静态函数的id
5.把所有的信息插入到结构体中
6.返回 true

java文件
public static String getBundleID() {
String packageName = "";
try {
// ---get the package info---
packageName = myContext.getPackageName();
if (packageName == null || packageName.length() <= 0) {
return "";
}
} catch (Exception e) {
Log.e("VersionInfo", "Exception", e);
}
return packageName;
}

c++调用部分
AppDelegate 文件 (不要忘记引用头文件)
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
auto myClass = new TestFun();
std::string myStr = myClass->getBundleID();
cocos2d::log("bundle id is %s",myStr.c_str());
#endif

Android.mk 文件 需要在 LOCAL_SRC_FILES 中添加
../../Classes/Test.mm \

执行编译

然而编译不过。。。

jni/../../Classes/AppDelegate.cpp:79: error: undefined reference to 'TestFun::TestFun()'
jni/../../Classes/AppDelegate.cpp:80: error: undefined reference to 'TestFun::getBundleID()'
jni/../../Classes/AppDelegate.cpp:83: error: undefined reference to 'TestFun::getStringWithParams()'
collect2: error: ld returned 1 exit status

更改Test.mm 为 Test.cpp 更改Android.mk 文件中写入的为
../../Classes/Test.cpp \

再次编译,,过了 完美

D/cocos2d-x debug info( 1794): TestFun create
D/cocos2d-x debug info( 1794): bundle id is org.cocos2dx.Hello

adb logcat 中日志文件

上面我们写了一个获取java 字符串的函数,如果我们想向java 中传递函数呢 ?
接着来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
.h 文件
#ifndef Test_hpp
#define Test_hpp

#include"cocos2d.h"

class TestFun {

public:
TestFun();
~TestFun();
std::string getStrFromOC();
std::string getBundleID();
<!-- 添加传递参数的函数 -->
std::string getStringWithParams();
};
#endif /* Test_hpp */

.cpp 文件
<!-- 实现部分比上面多了 getStringWithParams -->
std::string TestFun::getStringWithParams(){
std::string returnStr = "";
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
cocos2d::JniMethodInfo t;
if(cocos2d::JniHelper::getStaticMethodInfo(t, "org/cocos2dx/lua/AppActivity", "getMyString", "(Ljava/lang/String;)Ljava/lang/String;")){
cocos2d::log("getStringWithParams from c++");
<!-- 需要传递给java的字符串 -->
std::string toJavaStr = "from c++ params";
<!-- 需要转化为 jstring jni需要类型 -->
jstring stringArg1 = t.env->NewStringUTF(toJavaStr.c_str());
<!-- CallStaticObjectMethod 函数 第三个是需要传递的数据 -->
jstring myString = (jstring) t.env->CallStaticObjectMethod(t.classID, t.methodID,stringArg1);
returnStr = cocos2d::JniHelper::jstring2string(myString);
}
#endif
return returnStr;
}

AppDelegate 文件中修改
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
auto myClass = new TestFun();
std::string myStr = myClass->getBundleID();
cocos2d::log("bundle id is %s",myStr.c_str());
<!-- 新添加 getStringWithParams -->
std::string returnStr = myClass->getStringWithParams();
cocos2d::log("return str is %s",returnStr.c_str());
#endif

java 文件添加函数

public static String getMyString(String s) {
Log.d("c++ call getMyString params is ",s);
return "return form java ";
}


Android.mk 文件
依然依旧
../../Classes/Test.cpp \

编译通过

adb logcat
c++ call getMyString params is ----> java 文件内容
std::string toJavaStr = "from c++ params"; ----> c++文件传递给java
D/c++ call getMyString params is ( 1794): from c++ params
D/cocos2d-x debug info( 1794): return str is return form java

如上,通过.mm 文件可以直接调用oc 中函数,在cpp 文件中通过jni 可以调用java 也可以相互传递值

假如还想知道怎样从java 中调用c++ 该如何 ???好累,下次再写。。。

今天闲聊一些关于lua 与 native 之间相互调用的问题

lua 调用 oc

1.你一定知道我的使用场景是接入sdk对不对,嗯,大多数,我们需要使用ios设备系统提供的一些内容
很贴心,cocos提供了一个 luaBridge

lua部分代码

1
2
3
4
local luaBridge = require("cocos/cocos2d/luaoc")
--代码部分 调用静态方法 类的名字 方法名字 传递给oc的参数(以表的形式传输,OC用字典的方式来接收)
function luaoc.callStaticMethod(className, methodName, args)

函数返回值
当成功时,第一个值为 true,第二个值是 oc 方法的返回值(如果有)。
当失败时,第一个值为 false,第二个值是错误代码。

oc 部分代码 只是lua来调用oc部分

1
2
3
4
+ (void)functionName:(NSDictionary *)dict{
NSLog(@"lua calling functionName");
NSString *param = [dict valueForKey:@"paramName"];
}

oc 部分代码 lua来调用oc部分并且需要回调部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (void)functionName:(NSDictionary *)dict{
NSLog(@"lua calling functionName with callBack");
//luaCallBackID 先暂时这么写 因为一般我们会需要把 luaCallBackID 存放起来,等待需要的时候进行回掉
int luaCallBackID = (int)[[dict valueForKey:@"callBack"] integerValue];
}


//执行lua回调方法
- (void)exeLuaCallback:(int)Handler callbackMsg:(NSString *)result
{
if (Handler){
cocos2d::LuaBridge::pushLuaFunctionById(Handler); //压入需要调用的方法id
cocos2d::LuaStack *stack = cocos2d::LuaBridge::getStack(); //获取lua栈
stack->pushString([result UTF8String]);//放入需要给lua的参数
stack->executeFunction(1); //执行lua 函数 ,共有1个参数
cocos2d::LuaBridge::releaseLuaFunctionById(luaCallBackID); //执行完然后销毁
}
}

lua 和 java 的相互调用

同样很贴心,我们会发现 cocos 提供了 luaBridge

1
2
3
4
5
local callJavaStaticMethod = LuaJavaBridge.callStaticMethod
// 类的名字 静态方法名字 参数 方法签名
function luaj.callStaticMethod(className, methodName, args, sig)
//调用 Java 方法时,支持 int/float/boolean/String/Lua function 五种参数类型

函数返回值
当成功时,第一个值为 true,第二个值是 Java 方法的返回值(如果有)。
当失败时,第一个值为 false,第二个值是错误代码。

"javajnitype图片"

JNI Type Signatures : JNI Type Signatures

"常见java签名图片"

Java 部分代码

1
2
3
4
5
public static boolean isWXAppInstalled() {
Log.v("AppActivity", "isWXAppInstalled");
return wxApi.isWXAppInstalled();
}

相对应 lua 部分

1
2
3
local luaBridge = require("cocos/cocos2d/luaj")
local ok, ret = luaBridge.callStaticMethod("org/cocos2dx/lua/AppActivity", "isWXAppInstalled", nil, "()Z")

lua 调用 java 并传递回调

java 代码部分

1
2
3
4
5
6
7
8
9
10
11
12
//register  authCodeScriptHandler 跟oc 一样,需要暂存
public static void registerGetAuthCodeHandler(int scriptHandler) {
Log.v("BaseAppActivity", "registerGetAuthCodeHandler" + scriptHandler);
authCodeScriptHandler = scriptHandler;
}


//等到需要进行回调lua 时候

Cocos2dxLuaJavaBridge.callLuaFunctionWithString(authCodeScriptHandler, authCode);
Cocos2dxLuaJavaBridge.releaseLuaFunction(authCodeScriptHandler);

相对应 带有回调lua 部分

1
2
3
4
5
6
7

//传递callBack handler(self,self.callFun)
local luaBridge = require("cocos/cocos2d/luaj")
luaBridge.callStaticMethod("org/cocos2dx/lua/AppActivity", "sendAuthRequest", nil, "()V")
luaBridge.callStaticMethod("org/cocos2dx/lua/AppActivity", "registerGetAuthCodeHandler", {callback}, "(I)V")


很完美?然而并没有结束!!!

cocos2d-x for Android 运行在多线程环境下,所以在 Lua 和 Java 交互时需要注意选择适当的线程。
cocos2d-x 在 Android 上以两个线程来运行,分别是负责图像渲染的 GL 线程和负责 Android 系统用户界面的 UI 线程。

在 cocos2d-x 启动后,Lua 代码将由 GL 线程调用,因此从 Lua 中调用的 Java 方法如果涉及到系统用户界面的显示、更新操作,那么就必须让这部分代码切换到 UI 线程上去运行。

反之亦然,从 Java 调用 Lua 代码时,需要让这个调用在 GL 线程上执行,否则 Lua 代码虽然执行了,但会无法更新 cocos2d-x 内部状态。

"写入正确的回调lua的方法"

这才是要注意的坑!!!