Added by Steve Kim about 1 month ago
Recently I am porting an app to cocos2d-x which I made for android, ios, bada, and qnx.
And realized that there is no simple way to get json object asynchronously.
So here, I am posting a class I have made. Please give me a feedback if there are some errors. Thank you.
CCNetwork.h
#include "cocos2d.h"
#include "stdio.h"
#include "stdlib.h"
#include "curl.h"
#include "pthread.h"
#include "cJSON.h"
namespace cocos2d
{
class CCNetwork : public CCObject
{
SEL_CallFunc callback;
SelectorProtocol *target;
pthread_t threadInfo;
char url[260];
cJSON *cJSONResult;
CCNetwork()
{
cJSONResult = NULL;
}
protected:
static size_t writer( char *data, size_t size, size_t nmemb, string *writerData )
{
if(writerData == NULL)
return 0;
writerData->append(data, size * nmemb);
return size * nmemb;
}
public:
virtual ~CCNetwork()
{
if( cJSONResult != NULL )
{
freeResultJSON();
}
}
static void *run( void *arg )
{
CCNetwork *network = (CCNetwork *)arg;
CURL *curl;
CURLcode res;
int result = 1;
string buffer;
network->cJSONResult = NULL;
curl_global_init( CURL_GLOBAL_ALL );
curl = curl_easy_init();
if( curl )
{
curl_easy_setopt( curl, CURLOPT_URL, network->url );
curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1L );
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, CCNetwork::writer );
curl_easy_setopt( curl, CURLOPT_WRITEDATA, &buffer );
res = curl_easy_perform( curl );
curl_easy_cleanup( curl );
if (res == 0)
{
network->cJSONResult = cJSON_Parse( buffer.c_str() );
if( network->cJSONResult != NULL )
result = 0;
}
}
((network->target)->*(network->callback))();
return (void *)result;
}
cJSON *getResultJSON()
{
int success;
if( pthread_join( threadInfo, (void **)&success ) == 0 )
return cJSONResult;
else
return NULL;
}
void freeResultJSON()
{
if( cJSONResult != NULL )
{
cJSON_Delete( cJSONResult );
cJSONResult = NULL;
}
}
static CCNetwork *loadJSON( const char *url, SelectorProtocol *target, SEL_CallFunc callback )
{
CCNetwork *network = new CCNetwork();
if( network != NULL )
{
network->target = target;
network->callback = callback;
strcpy( network->url, url );
if( pthread_create( &network->threadInfo, NULL, CCNetwork::run, network ) == 0 )
{
network->autorelease();
return network;
}
CC_SAFE_DELETE( network );
}
return NULL;
}
};
}
callback function
void dataArrived() { isThreadRunning = false; }
the part which request url
isThreadRunning = true;
scheduleUpdate();
network = CCNetwork::loadJSON( "http://dodgemissile2.appspot.com/?count=10", this, callfunc_selector( YourNode::dataArrived ) );
network->retain();
void YourNode::update( ccTime dt )
{
if( !isThreadRunning )
{
if( network != NULL )
{
cJSON *result = network->getResultJSON();
// process with result here
network->release();
}
unscheduleUpdate();
}
}
It will be a very useful class.
My suggestions when reviewing the code.
1. char url[260];
is danger, especially you dont' check the length before strcpy( network->url, url ), stack will easily overflow. I prefer to use std::string here to make it safe.
2. You like to use several return points in a function. It's not good when you would like to write some profile and test the performance. For example:
static size_t writer( char *data, size_t size, size_t nmemb, string *writerData )
{
if(writerData == NULL)
return 0;
writerData->append(data, size * nmemb);
return size * nmemb;
}
static size_t writer( char *data, size_t size, size_t nmemb, string *writerData )
{
// profile_start
size_t retValue = 0;
if(writerData != NULL)
{
writerData->append(data, size * nmemb);
retValue = size * nmemb;
}
// profile_end. You have only one return point now
return retValue;
}