cocos2dx获取网络时间(二):浅析CCHttpClient

  在上一篇文章cocos2dx获取网络时间(一)中,我们使用CCHttpClient进行网络时间的数据请求和使用rapidjson解析Http请求得到的json数据 。在文章的最后 ,我留下了一个问题,怎么封装一个类来实现我们的方法。

  新建一个c++类 ,命名为NetTime,继承自CCNode并重写init()方法。然后引入CCHttpClient和rapidjson需要的头文件。我们的需求是可以返回NetTime的年,月,日,小时,分和秒,分别定义它们的private字段和public方法:

 1 #ifndef __NETTIME_H__

 2 #define __NETTIME_H__

 3 #include "cocos2d.h"

 4 #include "cocos-ext.h"

 5 #include "CocoStudio\Json\rapidjson\rapidjson.h"

 6 using namespace cocos2d;

 7 using namespace extension;

 8 using namespace rapidjson;

 9 

10 class NetTime:public CCNode

11 {

12 public:

13     CREATE_FUNC(NetTime);

14     int getYear(){

15         return _year;

16     };

17     int getMonth(){

18         return _month;

19     };

20     int getDay(){

21         return _day;

22     };

23     int getHour(){

24         return _hour;

25     };

26     int getMinute(){

27         return _minute;

28     };

29     int getSecond(){

30         return _second;

31     };

32     void getNetTime();

33     void requestNetTime();

34 private:

35     virtual bool init();

36     int _year, _month, _day, _hour, _minute, _second;

37     void onHttpComplete(CCHttpClient * sender, CCHttpResponse * response);

38     void readJson(std::string jsonStr);

39 };

40 #endif
View Code

  我们还需要一个requestNetTime方法来发起http请求,并在回调方法里面实现数据的获取和解析,直接把前面的代码拷贝过来即可:

 1 #include "NetTime.h"

 2 

 3 bool NetTime::init()

 4 {

 5     bool bRet = false;

 6     do

 7     {

 8         CC_BREAK_IF(!CCNode::init());

 9         

10         bRet = true;

11     } while (0);

12     return bRet;

13 }

14 

15 void NetTime::requestNetTime()

16 {

17     CCHttpRequest * request = new CCHttpRequest();

18     request->setUrl("http://115.159.3.250:1227/WebTime.svc/");

19     request->setRequestType(CCHttpRequest::kHttpGet);

20     request->setTag("WebTime");

21     request->setResponseCallback(this, httpresponse_selector(NetTime::onHttpComplete));

22     CCHttpClient::getInstance()->send(request);

23 }

24 

25 void NetTime::onHttpComplete(CCHttpClient * sender, CCHttpResponse * response)

26 {

27     CCHttpClient::getInstance()->release();

28     if (!response)

29         return;

30     if (0 != strlen(response->getHttpRequest()->getTag()))

31     {

32         CCLog("%s completed", response->getHttpRequest()->getTag());

33     }

34     int statusCode = response->getResponseCode();

35     char statusString[64] = {};

36     sprintf(statusString, "HTTP Status: %d, tag = %s", statusCode, response->getHttpRequest()->getTag());

37     CCLog("%s", statusString);

38     if (!response->isSucceed())

39     {

40         CCLog("response failed");

41         CCLog("error buffer:%s", response->getErrorBuffer());

42         return;

43     }

44     std::vector<char> * buffer = response->getResponseData();

45     std::string str;

46     for (unsigned i = 0; i < buffer->size(); i++)

47     {

48         char a = (*buffer)[i];

49         str.append(1, a);

50     }

51     CCLog("%s", str.c_str());

52     readJson(str);

53 }

54 

55 void NetTime::readJson(std::string jsonStr)

56 {

57     Document doc;

58     doc.Parse<0>(jsonStr.c_str());

59     if (!doc.IsObject())

60         return;

61     if (doc.HasMember("Year") && doc.HasMember("Month") && doc.HasMember("Day") && doc.HasMember("Hour") && doc.HasMember("Minute") && doc.HasMember("Second"))

62     {

63         _year = doc["Year"].GetInt();

64         _month = doc["Month"].GetInt();

65         _day = doc["Day"].GetInt();

66         _hour = doc["Hour"].GetInt();

67         _minute = doc["Minute"].GetInt();

68         _second = doc["Second"].GetInt();

69     }

70 }
View Code

  看到这里 ,读者可能会忍不住要说了,Leandro真是一个大忽悠,如此简单的类封装都要单独拿出来写一篇!

  各位客官莫着急,请接着向下看:

  我们回到HelloWorldScene.cpp的init方法,注释掉前面发起网络请求的代码,接着使用我们的NetTime类:

  

 1     /*    CCHttpRequest * request = new CCHttpRequest();

 2         request->setUrl("http://localhost:23244/NetTime.svc/Time");

 3         request->setRequestType(CCHttpRequest::kHttpGet);

 4         request->setTag("WebTime");

 5         request->setResponseCallback(this, httpresponse_selector(HelloWorld::onHttpComplete));

 6         CCHttpClient::getInstance()->send(request);*/

 7 

 8         NetTime * netTime = NetTime::create();

 9         netTime->requestNetTime();

10         char timeStr[50];

11         sprintf(timeStr, "NetTime  %d-%d-%d %d:%d:%d",

12             netTime->getYear(), netTime->getMonth(), netTime->getDay(),

13             netTime->getHour(), netTime->getMinute(), netTime->getSecond());

14         CCLabelTTF *timeLabel = CCLabelTTF::create(timeStr, "Arial", 18);

15         timeLabel->setPosition(ccp(240, 50));

16         this->addChild(timeLabel);
View Code

  进行调试:

  cocos2dx获取网络时间(二):浅析CCHttpClient

  异常信息为:变量timeStr内存损坏。可以分析得到引发此异常最可能的原因是netTime的getYear(),getMonth()...方法均没有返回正确的值。

  再来看上面NetTime类的代码,发现对_year,_month,_day,_hour,_minute,_second的赋值操作均在onHttpComplete()方法,也就是CCHttpClient网络请求的回调方法中。

  由此可以断定,在我们使用netTime的getYear(),getMonth()...方法返回字段值的时候 ,onHttpComplete()回调方法并没有执行。那么,为什么回调方法没有执行呢??

  通过查看Cocos2dx的api文档,发现对CCHttpClient有这样一句描述:处理异步http请求的单例模式 一旦请求完成,一个在生成请求时被提供的回调函数,会被发到主线程中

  到这里就恍然大悟了,原来CCHttpClient的http请求为异步方法,进行http请求并不会堵塞cocos2dx主线程的执行。

  这种情况下一个比较好的解决思路是,我们在onHttpComplete方法中对字段进行赋值之后,同样触发一个回调方法,在外部类需要读取网络时间的地方注册该回调方法。

  (对C++回调函数不熟悉的可以参考这里:C/C++之回调函数

  为了符合cocos2dx的使用习惯,我们参考cocos2dx中常用的回调方法设计,如CCHttpRequest中的回调。

  通过查看CCHttpRequest源码可以发现,首先声明了用于调用回调方法的函数指针和用于注册回调方法的宏:

    typedef void (CCObject::*SEL_HttpResponse)(CCHttpClient* client, CCHttpResponse* response);
       #define httpresponse_selector(_SELECTOR) (cocos2d::extension::SEL_HttpResponse)(&_SELECTOR) 

  setResponseCallback进行回调方法注册的实现如下,两个参数分别为回调方法的调用者和函数指针

   inline void setResponseCallback(CCObject* pTarget, SEL_HttpResponse pSelector)
       {
           _pTarget = pTarget;
           _pSelector = pSelector;
       
            if (_pTarget)
            {
                _pTarget->retain();
            }
       } 

  然后通过CCHttpClicent的send(CCHttpRequest* request)方法传入CCHttpRequest的实例,在异步请求Http完成后,调用注册的回调方法返回给我们Http请求的状态和数据。

  初步了解了cocos2dx中回调的实现后,回到我们的NetTime中,在NetTime.h头文件中声明函数指针和用于注册的宏定义: 

  

typedef void (CCObject::*SEL_NetTime)(WebTime * pSender);
#define netTime_selector(_SELECTOR) (SEL_NetTime)(&_SELECTOR)

  同时声明用于存放调用者实例和函数指针的字段:  

SEL_NetTime _pSelector;

CCObject * _pTarget;

  通过重载requestNetTime方法传入回调方法的调用者和函数指针: 

void NetTime::requestNetTime(CCObject * pTarget, SEL_NetTime pSelector)

{

    requestNetTime();

    _pTarget = pTarget;

    _pSelector = pSelector;

}

  在onHttpComplete方法的最后调用回调方法,并把NetTime的实例作为回调方法的参数:

  

 1 void NetTime::onHttpComplete(CCHttpClient * sender, CCHttpResponse * response)

 2 {

 3     CCHttpClient::getInstance()->release();

 4     if (!response)

 5         return;

 6     if (0 != strlen(response->getHttpRequest()->getTag()))

 7     {

 8         CCLog("%s completed", response->getHttpRequest()->getTag());

 9     }

10     int statusCode = response->getResponseCode();

11     char statusString[64] = {};

12     sprintf(statusString, "HTTP Status: %d, tag = %s", statusCode, response->getHttpRequest()->getTag());

13     CCLog("%s", statusString);

14     if (!response->isSucceed())

15     {

16         CCLog("response failed");

17         CCLog("error buffer:%s", response->getErrorBuffer());

18         return;

19     }

20     std::vector<char> * buffer = response->getResponseData();

21     std::string str;

22     for (unsigned i = 0; i < buffer->size(); i++)

23     {

24         char a = (*buffer)[i];

25         str.append(1, a);

26     }

27     CCLog("%s", str.c_str());

28     readJson(str);

29     if (_pTarget&&_pSelector)

30     {

31         (_pTarget->*_pSelector)(this);

32     }

33 }
View Code

  到这里便完成了对NetTime类的封装。

  再来修改HelloWorldSecne中对NetTime使用的代码:

  

1 NetTime * netTime = NetTime::create();

2 netTime->requestNetTime(this, netTime_selector(HelloWorld::onNetTimeComplete));
View Code

  onNetTimeComplete代码如下:

 1 void HelloWorld::onNetTimeComplete(NetTime * pSender)

 2 {

 3     char timeStr[50];

 4     sprintf(timeStr, "NetTime  %d-%d-%d %d:%d:%d",

 5         pSender->getYear(), pSender->getMonth(), pSender->getDay(),

 6         pSender->getHour(), pSender->getMinute(), pSender->getSecond());

 7     CCLabelTTF *timeLabel = CCLabelTTF::create(timeStr, "Arial", 18);

 8     timeLabel->setPosition(ccp(240, 50));

 9     this->addChild(timeLabel);

10 }
View Code

  调试结果: 

  cocos2dx获取网络时间(二):浅析CCHttpClient

  和上一篇文章的结果是一致的。

  Demo下载:NetTime(2).zip

你可能感兴趣的:(httpclient)