This is a bit different. I’ve posted about calling an activity, this one is about updating the UI thread. I’ve done UI update via async, handlers and threads, all of them fails. Openfeint handles the thread update so this is a very good example. You need openfeint api for this.
I’ve modified the Helloworld sample to test if this is only happening in the application we are developing. Sadly, the problem stays the same. I’m beginning to think this is a bug.
Here are the codes.
Native Side
AppDelegate.cpp
#include "AppDelegate.h"
#include "cocos2d.h"
#include "HelloWorldScene.h"
USING_NS_CC;
AppDelegate::AppDelegate()
{
}
AppDelegate::~AppDelegate()
{
}
bool AppDelegate::initInstance()
{
bool bRet = false;
do
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
// Initialize OpenGLView instance, that release by CCDirector when application terminate.
// The HelloWorld is designed as HVGA.
CCEGLView * pMainWnd = new CCEGLView();
CC_BREAK_IF(! pMainWnd
|| ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));
#endif // CC_PLATFORM_WIN32
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
// OpenGLView initialized in testsAppDelegate.mm on ios platform, nothing need to do here.
#endif // CC_PLATFORM_IOS
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
// OpenGLView initialized in HelloWorld/android/jni/helloworld/main.cpp
// the default setting is to create a fullscreen view
// if you want to use auto-scale, please enable view->create(320,480) in main.cpp
#endif // CC_PLATFORM_ANDROID
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WOPHONE)
// Initialize OpenGLView instance, that release by CCDirector when application terminate.
// The HelloWorld is designed as HVGA.
CCEGLView* pMainWnd = new CCEGLView(this);
CC_BREAK_IF(! pMainWnd || ! pMainWnd->Create(320,480, WM_WINDOW_ROTATE_MODE_CW));
#ifndef _TRANZDA_VM_
// on wophone emulator, we copy resources files to Work7/NEWPLUS/TDA_DATA/Data/ folder instead of zip file
cocos2d::CCFileUtils::setResource("HelloWorld.zip");
#endif
#endif // CC_PLATFORM_WOPHONE
#if (CC_TARGET_PLATFORM == CC_PLATFORM_AIRPLAY)
// MaxAksenov said it's NOT a very elegant solution. I agree, haha
CCDirector::sharedDirector()->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
#endif
bRet = true;
} while (0);
return bRet;
}
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());
// enable High Resource Mode(2x, such as iphone4) and maintains low resource on other devices.
// pDirector->enableRetinaDisplay(true);
// turn on display FPS
pDirector->setDisplayFPS(true);
// pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
// set FPS. the default value is 1.0/60 if you don't call this
pDirector->setAnimationInterval(1.0 / 60);
// create a scene. it's an autorelease object
CCScene *pScene = HelloWorld::scene();
// run
pDirector->runWithScene(pScene);
return true;
}
// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground()
{
CCDirector::sharedDirector()->pause();
// if you use SimpleAudioEngine, it must be pause
// SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();
}
// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground()
{
CCDirector::sharedDirector()->resume();
// if you use SimpleAudioEngine, it must resume here
// SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
}
AppDelegate.h
#ifndef _APP_DELEGATE_H_
#define _APP_DELEGATE_H_
#include "CCApplication.h"
/**
@brief The cocos2d Application.
The reason for implement as private inheritance is to hide some interface call by CCDirector.
*/
class AppDelegate : private cocos2d::CCApplication
{
public:
AppDelegate();
virtual ~AppDelegate();
/**
@brief Implement for initialize OpenGL instance, set source path, etc...
*/
virtual bool initInstance();
/**
@brief Implement CCDirector and CCScene init code here.
@return true Initialize success, app continue.
@return false Initialize failed, app terminate.
*/
virtual bool applicationDidFinishLaunching();
/**
@brief The function be called when the application enter background
@param the pointer of the application
*/
virtual void applicationDidEnterBackground();
/**
@brief The function be called when the application enter foreground
@param the pointer of the application
*/
virtual void applicationWillEnterForeground();
};
#endif // _APP_DELEGATE_H_
HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "JNISharedObject.h"
USING_NS_CC;
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::node();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::node();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
CCMenuItemImage *pCloseItem = CCMenuItemImage::itemFromNormalImage(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(HelloWorld::menuCloseCallback) );
pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20) );
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::menuWithItems(pCloseItem, NULL);
pMenu->setPosition( CCPointZero );
this->addChild(pMenu, 1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
CCLabelTTF* pLabel = CCLabelTTF::labelWithString("Hello World", "Thonburi", 34);
// ask director the window size
CCSize size = CCDirector::sharedDirector()->getWinSize();
// position the label on the center of the screen
pLabel->setPosition( ccp(size.width / 2, size.height - 20) );
// add the label as a child to this layer
this->addChild(pLabel, 1);
// add "HelloWorld" splash screen"
CCSprite* pSprite = CCSprite::spriteWithFile("HelloWorld.png");
// position the sprite on the center of the screen
pSprite->setPosition( ccp(size.width/2, size.height/2) );
// add the sprite as a child to this layer
this->addChild(pSprite, 0);
return true;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
JNISharedObject::Inst()->unlockAchievement("1144912 ");
}
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class HelloWorld : public cocos2d::CCLayer
{
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
// a selector callback
virtual void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
LAYER_NODE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
InitJNIFunctions.cpp
/*
* InitJNIFunctions.cpp
*
* Created on: Jul 25, 2011
* Author: beejay
*/
#include "InitJNIFunctions.h"
#include "JNISharedObject.h"
JNIEXPORT void JNICALL Java_org_cocos2dx_testgame_testgame_initCallbacks(JNIEnv* env, jobject thiz)
{
__android_log_write(ANDROID_LOG_ERROR,"Test","Message");
JNISharedObject::sharedEnv = env;
JNISharedObject::obj = thiz;
JNISharedObject::jcls = env->GetObjectClass(thiz);
}
InitJNIFunctions.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class org_cocos2dx_PitCrewCombine_PitCrewCombine */
#ifndef _Included_org_cocos2dx_testgame_testgame
#define _Included_org_cocos2dx_testgame_testgame
#ifdef __cplusplus
extern "C" {
#endif
#undef org_org_cocos2dx_testgame_testgame_HANDLER_SHOW_DIALOG
#define org_org_cocos2dx_testgame_testgame_HANDLER_SHOW_DIALOG 1L
/*
* Class: org_cocos2dx_PitCrewCombine_PitCrewCombine
* Method: initCallbacks
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_cocos2dx_testgame_testgame_initCallbacks
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
/* Header for class org_cocos2dx_PitCrewCombine_PitCrewCombine_LoginDialogListener */
#ifndef _Included_org_cocos2dx_testgame_testgame_LoginDialogListener
#define _Included_org_cocos2dx_testgame_testgame_LoginDialogListener
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif
/* Header for class org_cocos2dx_PitCrewCombine_PitCrewCombine_WallPostDialogListener */
#ifndef _Included_org_cocos2dx_testgame_testgame_WallPostDialogListener
#define _Included_org_cocos2dx_testgame_testgame_WallPostDialogListener
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif
JNISharedObject.cpp
/*
* JNISharedObject.cpp
*
* Created on: Jul 16, 2011
* Author: beejay
*/
#include "JNISharedObject.h"
#include
JNISharedObject* JNISharedObject::pInstance = NULL;
JNIEnv* JNISharedObject::sharedEnv = NULL;
jclass JNISharedObject::jcls = NULL;
jobject JNISharedObject::obj = NULL;
JNISharedObject* JNISharedObject::Inst(){
if(pInstance == NULL){
pInstance = new JNISharedObject();
}
return pInstance;
}
void JNISharedObject::startFacebook(const char* share)
{
jmethodID mid = sharedEnv->GetMethodID(jcls, "share", "(Ljava/lang/String;)V");
if (mid == 0) return;
sharedEnv->CallVoidMethod(obj, mid, sharedEnv->NewStringUTF(share));
}
void JNISharedObject::achievementList()
{
jmethodID mid = sharedEnv->GetMethodID(jcls, "achievementList", "()V");
if (mid == 0) return;
sharedEnv->CallVoidMethod(obj, mid);
}
void JNISharedObject::leaderBoards()
{
jmethodID mid = sharedEnv->GetMethodID(jcls, "leaderBoards", "()V");
if (mid == 0) return;
sharedEnv->CallVoidMethod(obj, mid);
}
void JNISharedObject::unlockAchievement(const char* id)
{
jmethodID mid = sharedEnv->GetMethodID(jcls, "unlockAchievement", "(Ljava/lang/String;)V");
if (mid == 0) return;
sharedEnv->CallVoidMethod(obj, mid, sharedEnv->NewStringUTF(id));
}
JNISharedObject::JNISharedObject(){ // constructor
}
JNISharedObject.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#ifndef JNISHARED__
#define JNISHARED__
#include "cocos2d.h"
#include
#include
#include
#include
#include
using namespace std;
#define kMaxUnlockedCars 50
#define kMaxCars 4
/* Header for class org_cocos2dx_PitCrewCombine_PitCrewCombine */
class JNISharedObject
{
public:
static JNISharedObject* Inst();
void Logging(string message);
void startFacebook(const char*);
void achievementList();
void unlockAchievement(const char* id);
void leaderBoards();
static JNIEnv* sharedEnv;
static jobject obj;
static jclass jcls;
static JNISharedObject* pInstance;
void loadValuesFromSaveFile();
void saveValuesFromSaveFile();
static float maComponentTimes[6];
static float maPitStopTimes[10];
static float maSkillJackTimes[4];
static float maSkillTireTimes[4];
static float maSkillFuelTimes[4];
static int maUnlockedcars[kMaxUnlockedCars];
static int maNewCars[kMaxUnlockedCars];
static int nCarType;
static bool bIsSoundOn;
static bool bIsFirstPlay;
static float bestOnlineScores[7];
static int nPit11Secs3InARow;
static int nPit15Secs5InARow;
static int nPit12Secs5InARow;
static int nCompletedPit;
static int nGCWin;
static bool bSkillGasDone;
static bool bSkillJackDone;
static bool bSkillTiresDone;
static int nCarP1;
static int nCarP2;
static int nNightRacer;
static int nNextScene;
void Initialize();
~JNISharedObject();
protected:
JNISharedObject(); // constructor
};
#endif
Java Side: org.cocos2dx.testgame
testapp.java
package org.cocos2dx.testgame;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Application;
import android.util.Log;
import com.openfeint.api.OpenFeint;
import com.openfeint.api.OpenFeintDelegate;
import com.openfeint.api.OpenFeintSettings;
import com.openfeint.api.resource.Achievement;
import com.openfeint.api.resource.Leaderboard;
public class testapp extends Application {
public static List achievements = null;
public static List leaderboards = null;
@Override
public void onCreate() {
super.onCreate();
Map options = new HashMap();
options.put(OpenFeintSettings.SettingCloudStorageCompressionStrategy, OpenFeintSettings.CloudStorageCompressionStrategyDefault);
// use the below line to set orientation
// options.put(OpenFeintSettings.RequestedOrientation, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Log.e("Init Openfeint", "Settings Init");
OpenFeintSettings settings = new OpenFeintSettings("PCC", "mRyz1yYCsOTH6KpLcK1IQ", "jy6vangncM4PkLQS1nw5KMRFBK3fCUoCMnV1VJ2PY", "332553", options);
Log.e("Init Openfeint", "App init");
OpenFeint.initialize(this, settings, new OpenFeintDelegate() { });
// Achievement.list(new Achievement.ListCB() {
// @Override public void onSuccess(List _achievements) {
// achievements = _achievements;
// }
// });
//
// Leaderboard.list(new Leaderboard.ListCB() {
// @Override public void onSuccess(List _leaderboards) {
// leaderboards = _leaderboards;
// }
// });
}
}
testgame.java
package org.cocos2dx.testgame;
import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
import org.cocos2dx.testgame.R;
import com.openfeint.api.resource.Achievement;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
public class testgame extends Cocos2dxActivity{
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// get the packageName,it's used to set the resource path
String packageName = getApplication().getPackageName();
super.setPackageName(packageName);
initCallbacks();
setContentView(R.layout.game_demo);
mGLView = (Cocos2dxGLSurfaceView) findViewById(R.id.game_gl_surfaceview);
// Get the size of the mGLView after the layout happens
mGLView.post(new Runnable() {
@Override
public void run() {
Cocos2dxActivity.screenHeight = mGLView.getHeight();
Cocos2dxActivity.screenWidth = mGLView.getWidth();
}
});
}
public void unlockAchievement(String id)
{
final Achievement a = new Achievement(id);
a.unlock(new Achievement.UnlockCB()
{
public void onSuccess(boolean newUnlock) {
Log.e("In Achievement", "Unlock");
a.notifyAll();
}
});
}
@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
private GLSurfaceView mGLView;
static {
System.loadLibrary("cocos2d");
System.loadLibrary("cocosdenshion");
System.loadLibrary("game");
}
private native void initCallbacks();
}
manifest File I’m Using
The warning I get
08-09 16:54:00.645: WARN/WindowManager(3461): Window Window{40af7920 org.cocos2dx.testgame/org.cocos2dx.testgame.testgame paused=false} destroyed surface Surface(name=org.cocos2dx.testgame/org.cocos2dx.testgame.testgame, identity=-1, mNativeSurface=0), session Session{40951cf8 uid 10174}
08-09 16:54:00.650: WARN/WindowManager(3461): Window Window{407dcb98 SurfaceView paused=false} destroyed surface Surface(name=SurfaceView, identity=-1, mNativeSurface=0), session Session{40951cf8 uid 10174}
08-09 16:54:00.680: WARN/SurfaceFlinger(3461): org.cocos2dx.testgame/org.cocos2dx.testgame.testgame[504] is not removed from mLayerMap
08-09 16:54:00.680: WARN/SurfaceFlinger(3461): SurfaceView[505] is not removed from mLayerMap
See that the window gets destroyed?
————
I know that native to java and vice-versa calls are working because I’ve tested this with calls that does not need to update the UI.
I hope you could look into this. I might be forced to port Cocos2d-x code to Cocos2d-android if no fix could be found, and that’s a lot of work.
Thanks.