首页 > 代码库 > 从Objective-C转战C++ Android平台开发实践(C++/Java)
从Objective-C转战C++ Android平台开发实践(C++/Java)
是否使用虚拟方法
- 最好在不用“virtual”关键字的情况下声明所有cpp成员方法
- 但是在写CPP头文件时,请检查有没有父类的方法被当前的工作覆盖。如果有,请确保将这些方法改为虚拟方法。
- 如果从父类继承了一个虚拟方法,确保这个方法可以继承“virtual”(虚拟)关键字
public/protected/private方法介绍
- 默认情况下,将所有成员方法声明为“public”(公共)
- 如果以下任意条件满足,方法必须为“private”:该方法在.m文件被声明;该方法位于“private”范畴
public/protected/private成员变量
- 声明所有成员变量为“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 class CCar { public : CCar(); bool init(); virtual ~CCar(); protected : CEngine* m_pEngine; bool m_bStarted; bool m_bLocked; }; // 1st-phase construction // only set the default value & alloc memory CCar::CCar() :m_pEngine( new CEngine) ,m_bStarted( false ) ,m_bLocked( true ) { printf ( "CCar constructor\n" ); } // 2st-phase construction // do logical initialization bool CCar::init() { bool bRet = 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" ); return bRet; } // destruction CCar::~CCar() { if (m_pEngine) { delete m_pEngine; m_pEngine = NULL; } printf ( "CCar destructor\n" ); } // invoker int _tmain( int argc, _TCHAR* argv[]) { // in heap CCar* pCar = new CCar; if (!pCar->init()) { CCX_SAFE_DELETE(pCar); } // in stack CCar car; if (!car.init()) { // do sth. } return 0; } |
下载样本代码请参见附件“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 : virtual varType 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 : virtual varType get##funName( void );\ public : virtual void set##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 : inline varType get##funName( void ){ return varName; } /** 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 : inline varType get##funName( void ){ return varName; }\ public : inline void set##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 | class CTest { bool foo(); } |
目的
在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 void HelloWorld::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 void HelloWorld::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. */ public void onCreate(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 public void SampleSelector(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 = new AlertDialog.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。 祝编程愉快!
从Objective-C转战C++ Android平台开发实践(C++/Java)