从Objective-C转战C++ Android平台开发实践,C++/Java

是否使用虚拟方法

  1. 最好在不用“virtual”关键字的情况下声明所有cpp成员方法
  2. 但是在写CPP头文件时,请检查有没有父类的方法被当前的工作覆盖。如果有,请确保将这些方法改为虚拟方法。
  3. 如果从父类继承了一个虚拟方法,确保这个方法可以继承“virtual”(虚拟)关键字

public/protected/private方法介绍

  1. 默认情况下,将所有成员方法声明为“public”(公共)
  2. 如果以下任意条件满足,方法必须为“private”:该方法在.m文件被声明;该方法位于“private”范畴

public/protected/private成员变量

  1. 声明所有成员变量为“protected”,没有其他选择。

两阶段构造

如何

  • 第一阶段:在构造器初始化列表中设置所有成员变量的默认值。但不要在构造器中编写任何逻辑init(初始化)。
  • 第二阶段:在“CMyClass* init(...)”函数中编写逻辑init(初始化)。如果初始化失败会返回NULL。

为什么

  • 我们决定在C++中不再使用捕获异常机制(try-catch exception mechanism)。这是为了减少足迹和二进制大小。因此在C++构造中发生的任何异常都不会被报告给调用者。

时间

  • 两阶段构造并不是在每个类中都一定要进行,只是在那些存在初始化逻辑步骤的类中进行。换言之,在构造器中编写逻辑初始化是禁止的,这种情况可能会返回错误。

调用者须知

  • 如何你调用的类有“bool init(...)”函数,请在构造之后立即调用该函数。

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

#define CCX_BREAK_IF(cond) if(cond) break;

#define CCX_SAFE_DELETE(p) if(p) {delete (p); (p) = NULL;}

// declaration

classCCar

{

public:

CCar();

boolinit();

virtual~CCar();

protected:

CEngine* m_pEngine;

boolm_bStarted;

boolm_bLocked;

};

// 1st-phase construction

// only set the default value & alloc memory

CCar::CCar()

:m_pEngine(newCEngine)

,m_bStarted(false)

,m_bLocked(true)

{

printf("CCar constructor\n");

}

// 2st-phase construction

// do logical initialization

boolCCar::init()

{

boolbRet =false;

do

{

m_bLocked =false;

CCX_BREAK_IF( !m_pEngine );// defensive

CCX_BREAK_IF( !m_pEngine->start() );// logic

// success

bRet =true;

}while(0);

printf("CCar init\n");

returnbRet;

}

// destruction

CCar::~CCar()

{

if(m_pEngine)

{

deletem_pEngine;

m_pEngine = NULL;

}

printf("CCar destructor\n");

}

// invoker

int_tmain(intargc, _TCHAR* argv[])

{

// in heap

CCar* pCar =newCCar;

if(!pCar->init())

{

CCX_SAFE_DELETE(pCar);

}

// in stack

CCar car;

if(!car.init())

{

// do sth.

}

return0;

}

下载样本代码请参见附件“TwoPhaseConstruction.zip”。该项目已经在Win32环境+VS2008测试过。

objc属性

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

/** CCX_PROPERTY_READONLY is used to declare a protected variable.

We can use get method to read the variable.

@param varType : the type of variable.

@param varName : variable name.

@param funName : "get + funName" is the name of the get method.

@warning : The get method is a public virtual function, you should override it first.

The variables and methods declared after CCX_PROPERTY_READONLY are all public.

If you need protected or private, please declare.

*/

#define CCX_PROPERTY_READONLY(varType, varName, funName)\

protected: varType varName;\

public:virtualvarType get##funName(void);

/** CCX_PROPERTY is used to declare a protected variable.

We can use get method to read the variable, and use the set method to change the variable.

@param varType : the type of variable.

@param varName : variable name.

@param funName : "get + funName" is the name of the get method.

"set + funName" is the name of the set method.

@warning : The get and set methods are public virtual functions, you should override them first.

The variables and methods declared after CCX_PROPERTY are all public.

If you need protected or private, please declare.

*/

#define CCX_PROPERTY(varType, varName, funName)\

protected: varType varName;\

public:virtualvarType get##funName(void);\

public:virtualvoidset##funName(varType var);

/** CCX_SYNTHESIZE_READONLY is used to declare a protected variable.

We can use get method to read the variable.

@param varType : the type of variable.

@param varName : variable name.

@param funName : "get + funName" is the name of the get method.

@warning : The get method is a public inline function.

The variables and methods declared after CCX_SYNTHESIZE_READONLY are all public.

If you need protected or private, please declare.

*/

#define CCX_SYNTHESIZE_READONLY(varType, varName, funName)\

protected: varType varName;\

public:inlinevarType get##funName(void){returnvarName; }

/** CCX_SYNTHESIZE is used to declare a protected variable.

We can use get method to read the variable, and use the set method to change the variable.

@param varType : the type of variable.

@param varName : variable name.

@param funName : "get + funName" is the name of the get method.

"set + funName" is the name of the set method.

@warning : The get and set methods are public inline functions.

The variables and methods declared after CCX_SYNTHESIZE are all public.

If you need protected or private, please declare.

*/

#define CCX_SYNTHESIZE(varType, varName, funName)\

protected: varType varName;\

public:inlinevarType get##funName(void){returnvarName; }\

public:inlinevoidset##funName(varType var){ varName = var; }

id

Objc中一些函数会返回“ID“,在转换成CPP后就会返回“bool”。在Objc中,你可以像“[[MyClass alloc] init] autorelease]”一样编写代码,无需在意初始化是否失败返回NULL。在这种情况下“[nil autorelease]”不会使程序崩溃。但是在CPP中,返回“bool”是为了防止开发人员编写“pClass = (new MyClass())->init()->foo()”。如果初始化失败返回NULL,在CPP中“null->fool()”会崩溃然后跳出程序。另一方面,如果“foo()”返回值不是“MyClass*”,例如返回“bool”,那调用者就会失去“new MyClass”的指针,然后无法从堆栈(heap)中删除指针。这就会很危险。

1

2

@interface CTest

-(id) foo();

以上代码必须转换为

1

2

3

4

classCTest

{

boolfoo();

}

目的

在Cocos2dx场景中点击按钮,即可向本地平台Java弹出对话框发送信息。详见本文。

指令

你需要对项目执行几次include(包含)指令,本人已创建一个在线Repo库,根据开发环境种类分成了几个部分。请确保include(包含)了所有C++和Java的文件。在线Repo连接如下:EasyNDK。

从C++包含

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

#include "NDKHelper.h"

// The button click method of Cocos2dx

voidHelloWorld::menuCloseCallback(CCObject* pSender)

{

// Register a selector in a global space

// So that when our native environment will call the method with the string

// It can respond to the selector

// Note : Group name is there for ease of removing the selectors

NDKHelper::AddSelector("HelloWorldSelectors",

"SampleSelector",

callfuncND_selector(HelloWorld::SampleSelector),

this);

// Making parameters for message to be passed to native language

// For the ease of use, i am sending the method to be called name from C++

CCDictionary* prms = CCDictionary::create();

prms->setObject(CCString::create("SampleSelector"),"to_be_called");

// Finally call the native method in current environment

SendMessageWithParams(string("SampleSelector"), prms);

}

// A selector that will respond to us, when native language will call it

voidHelloWorld::SampleSelector(CCNode *sender,void*data)

{

CCLog("Called from native environment");

}

// Destructor to remove all the selectors which are grouped as HelloWorldSelectors

HelloWorld::~HelloWorld()

{

// Remove the associated selector group from the global space,

// Because we are destroying this instance

NDKHelper::RemoveSelectorsInGroup("HelloWorldSelectors");

}

从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

/** Called when the activity is first created. */

publicvoidonCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

// In the main Activity, assigning that activity as a receiver for C++ messages

AndroidNDKHelper.SetNDKReciever(this);

}

// Implement the method to be called for a message from C++

// Be sure to name the method to be of the same string as you will pass from C++

// Like we passed "SampleSelector" from C++, that is why created this method

publicvoidSampleSelector(JSONObject prms)

{

Log.v("SampleSelector","purchase something called");

Log.v("SampleSelector","Passed params are : "+ prms.toString());

String CPPFunctionToBeCalled = null;

try

{

CPPFunctionToBeCalled = prms.getString("to_be_called");

}

catch(JSONException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

AlertDialog.Builder builder =newAlertDialog.Builder(this);

builder.setMessage("This is a sample popup on Android").

setTitle("Hello World!").

setNeutralButton("OK", null).show();

// Send C++ a message with paramerts

// C++ will recieve this message, only if the selector list will have a method

// with the string we are passing

AndroidNDKHelper.SendMessageWithParameters(CPPFunctionToBeCalled, null);

}

注意 若连接其他SDK,你可以参考相关SDK的Java指南并分别实施消息传递机制从Cocos2d-x进行调用。本人通过这种方法已经实现了AppCircle、Flurry以及其他很多SDK。 拥有完整源码的样本项目可从网上下载:Sample Android Project。 祝编程愉快!