Is it possible to call java code in cocos2dx project?


#1

I’m using cocos2d-x 1.0.1 , and after building the project the structure looks like below:

src
gen
Android 4.0.3
Android Dependencies
assets
bin
Classes
jni
libs
obj
res

My question is, is it possible to add some other java classes which could be called somewhere when cocos2d-x is running?

BTW, my root problem is that I’m trying to connect to a Java server via socket, but it failed… It will be appreciated if someone could help on this issue too :slight_smile:


#2

Using JNI it’s possible.

You can call c*+ code from java, and also java code from c*+.


#3

Thanks for your reply:)

when adding a java class, if it requires a third party jar(e.g. mina-core-2.0.4.jar), how do I import it to a cocos2d-x project?
As I’m developing cocos2dx project in eclipse, I tried to import the jar files in “Java Build Path” - “Libraries” like a usual java project, and it still throws a NoClassFoundError when calling java code via JNI.


#4

_ As I’m developing cocos2dx project in eclipse, I tried to import the jar files in “Java Build Path” - “Libraries” like a usual java project, and it still throws a NoClassFoundError when calling java code via JNI._
> I think “NoClassFoundException” is a java side problem.
What version of ADT are you using?
If you are using version 17 and over, you should check the libraries that you are using in “java Build Path”
“Order and Export”.


#5

I recently tried to use JNI interface since my project target both iOS and Androïd platform.
I looked to the SimpleAudioEngine example as it’s interfaced with JNI but I couldn’t find a way to make what I need.

I would like to get device informations like the model and the os version of the platform so I create a DeviceDetection class in java like this :

`package com.seventhside;
import android.util.DisplayMetrics;

public class DeviceDetection {
public DeviceDetection()
{
DisplayMetrics metrics = new DisplayMetrics();

    _deviceSystemName = android.os.Build.VERSION.CODENAME;
    _deviceSystemVersion = android.os.Build.VERSION.RELEASE;
    _deviceModel = android.os.Build.MODEL;
    _deviceResolutionWidth = metrics.widthPixels;
    _deviceResolutionHeight = metrics.heightPixels;
}

private String _deviceSystemName;
private String _deviceSystemVersion;
private String _deviceModel;
private int _deviceResolutionWidth;
private int _deviceResolutionHeight;

//Getters-------------------------------------------------
public String deviceSystemName()
{
    return _deviceSystemName;
}
public String deviceSystemVersion()
{
    return _deviceSystemVersion;
}
public String deviceModel()
{
    return _deviceModel;
}
public int deviceResolutionWidth()
{
    return _deviceResolutionWidth;
}

public int deviceResolutionHeight()
{
    return _deviceResolutionHeight;
}

}`

Then a create the .h and .cpp file to wrap with JNI env. For the .h with juste a function to get the SystemName :

`#include <jni.h>

extern “C”
{
extern void deviceSystemNameJNI();
}`

and for the .cpp inspired from what I’ve found in SimpleAudioEngine :

`#include “DeviceDetectionJNI.h”
#include “platform/android/jni/JniHelper.h”
#include <jni.h>
#include <android/log.h>

#define LOG_TAG “deviceDetection”
#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,VA_ARGS)

static JNIEnv* getJNIEnv(void)
{

JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
if (NULL == jvm) {
    LOGD("Failed to get JNIEnv. JniHelper::getJavaVM() is NULL");
    return NULL;
}

JNIEnv *env = NULL;
// get jni environment
jint ret = jvm->GetEnv((void**)&env, JNI_VERSION_1_4);
}

}

static jclass getClassID(JNIEnv *pEnv)
{
jclass ret = pEnv->FindClass(CLASS_NAME);
if (! ret)
{
LOGD(“Failed to find class of %s”, CLASS_NAME);
}

    return ret;

}

static bool getStaticMethodInfo(JniMethodInfo &methodinfo, const char *methodName, const char *paramCode)
{
jmethodID methodID = 0;
JNIEnv *pEnv = 0;
bool bRet = false;

    do
    {
        pEnv = getJNIEnv();
        if (! pEnv)
        {
            break;
        }

        jclass classID = getClassID(pEnv);

        methodID = pEnv->GetStaticMethodID(classID, methodName, paramCode);
        if (! methodID)
        {
            LOGD("Failed to find static method id of %s", methodName);
            break;
        }

        methodinfo.classID = classID;
        methodinfo.env = pEnv;
        methodinfo.methodID = methodID;

        bRet = true;
    } while (0);

    return bRet;

}

//DeviceDetection methods
void deviceSystemNameJNI()
{
JniMethodInfo methodInfo;

  if (! getStaticMethodInfo(methodInfo, "deviceSystemName", "()V"))
        {
            return;
        }

  methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
  methodInfo.env->DeleteLocalRef(methodInfo.classID);

}

Then I add the DeviceDetection.h and .cpp to allow C++ calls to this :

#include “Export.h”

class EXPORT_DLL DeviceDetection
{
public:
DeviceDetection();
~DeviceDetection();

static DeviceDetection* sharedDeviceDetection();

static void end();

void deviceSystemName();

};`

and

`#include “DeviceDetection.h”
#include “DeviceDetectionJNI.h”

static DeviceDetection* pDeviceDetection = nullptr;

DeviceDetection* DeviceDetection::sharedDeviceDetection()
{
if (! pDeviceDetection)
{
pDeviceDetection = new DeviceDetection();
}

return pDeviceDetection;

}

void DeviceDetection::deviceSystemName()
{
deviceSystemNameJNI();
}

void DeviceDetection::end()
{
endJNI();
}`

But I don’t exactly find what to do in my main.cpp to get my values so I tried with
DeviceDetection::sharedDeviceDetection()~~>deviceSystemName;
but Eclipse says :
Multiple markers at this line
~~ expected constructor, destructor, or type conversion before
‘deviceSystemName’

  • expected type-specifier before ‘deviceSystemName’

And lastly I tried :
DeviceDetection* myInfo = DeviceDetection::sharedDeviceDetection();
myInfo->deviceSystemName();

But this leads to a “myInfo” does not name a type.

I currently have some difficulties to get that working. Sorry for the presentation but Inline coding does weird things :o
Could someone help on what’s wrong ?


#6

Any ideas ?

To be simple, just on this part of the code in my main.cpp :
@
DeviceDetection* myInfo = DeviceDetection::sharedDeviceDetection();
myInfo~~>pDeviceDetection = 0;
@
Eclipse give me a "Multiple markers at this line~~ ‘myInfo’ does not name a type - Syntax error
But myInfo is explicitly defined, why this errors ?


#7

if (! getStaticMethodInfo(methodInfo, “deviceSystemName”, “()V”))
{
return;
}
>

-> what is getStaticMethodInfo() method? If that is a method of JniHelper, then you should use like following code.

JniHelper::getStaticMethodInfo(methodInfo, “pakage/class”, “deviceSystemName”, “()V”)

if your package name is “com.domain.test” and class name is “MyClass” then you should pass “com/domain/test/MyClass” to the second argument of getStaticMethodInfo method.
eg.) JniHelper::getStaticMethodInfo(methodInfo, “com/domain/test/MyClass”, “deviceSystemName”, “()V”)


#8

Ok thank you for this DongSoo Moon.
There is probably others odd things in my configuration but it’s very hard to point it for me.

Anyway, I have always the strange behavior with the type name that is not recognized.
Should it comes from de JNI cpp files ?


#9

Anyway, I have always the strange behavior with the type name that is not recognized.

-> sorry, I can’t understand exactly what you are saying…


#10

In my main.cpp file, I try something like this to be able to get infos from my DeviceDetection.java class :

DeviceDetection* myInfo = DeviceDetection::sharedDeviceDetection();
myInfo~~>pDeviceDetection = 0;
But Eclipse prompt an error saying myInfo is not a type name. But I defined it as a DeviceDetection pointer just before so I don’t understand what it means.
My goal is to get differents datas about the platform environment from my native java class and use them in C++ .
Here is the java class where I pick the infos from :
@
package com.seventhside;
import android.util.DisplayMetrics;
public class DeviceDetection {
public DeviceDetection
{
DisplayMetrics metrics = new DisplayMetrics;
*deviceSystemName = android.os.Build.VERSION.CODENAME;
*deviceSystemVersion = android.os.Build.VERSION.RELEASE;
*deviceModel = android.os.Build.MODEL;
*deviceResolutionWidth = metrics.widthPixels;
deviceResolutionHeight = metrics.heightPixels;
}
private String
deviceSystemName;
private String deviceSystemVersion;
private String
deviceModel;
private int deviceResolutionWidth;
private int
deviceResolutionHeight;
//Getters————————————————~~
public String deviceSystemName()
{
return deviceSystemName;
}
public String deviceSystemVersion
{
return
deviceSystemVersion;
}

public String deviceModel()
{
return deviceModel;
}
public int deviceResolutionWidth
{
return
deviceResolutionWidth;
}

public int deviceResolutionHeight()
{
return _deviceResolutionHeight;
}
}
@

I want getters to be accessible via JNI methods.


#11

you are using NOT STATIC methods in java class.
so, you should use JniHelper::getMethodInfo() instead of JniHelper::getStaticMethodInfo().

eg)
in jni cpp file….

int getDeviceResolutionWidth_jni() {
JniMethodInfo t;

if(JniHelper::getMethodInfo(t, “com/seventhside/DeviceDetection”, “deviceResolutionWidth”, “()I”)) {
jint result = t.env~~>CallIntMethod;
t.env~~>DeleteLocalRef(t.classID);

return result;
}

return –1; //failed to getMethodInfo…
}


#12

Okay, I have a better understanding of that :slight_smile:

Now my JNI class looks like this :
@
jstring deviceSystemNameJNI()
{
JniMethodInfo methodInfo;

if (! getMethodInfo(methodInfo, “com/seventhside/Stargoalz2dx/DeviceDetection”, “deviceSystemName”, “()V”))
{
jstring result = methodInfo.env~~>CallStringMethod;
methodInfo.env~~>DeleteLocalRef(methodInfo.classID);
return result;
}
return –1;
}
@

And I also removed the static word where it’s needed as you said.

Now I want to get my deviceSystemName in my main.cpp to log it in the console output.
So I have a DeviceDetection.h like this :
@
using namespace cocos2d;
#include “Export.h”

class EXPORT_DLL DeviceDetection
{
public:
DeviceDetection();
DeviceDetection;
\ static\ DeviceDetection*\ sharedDeviceDetection;
\ static\ void\ end;
\ CCString*\ deviceSystemName;
};
@
And\ the\ cpp\ with\ it\ :
@
#include\ “DeviceDetection.h”
#include\ “DeviceDetectionJNI.h”

static\ DeviceDetection*\ pDeviceDetection\ =\ nullptr;
DeviceDetection::DeviceDetection
{
}
DeviceDetecton::DeviceDetection()
{

}

static DeviceDetection* DeviceDetection::sharedDeviceDetection()
{
if (! pDeviceDetection)
{
pDeviceDetection = new DeviceDetection();
}

return pDeviceDetection;
}

CCString* DeviceDetection::deviceSystemName()
{
return deviceSystemNameJNI();
}

void DeviceDetection::end()
{
endJNI();
}
@

I used a CCString* to store the data from the jstring returned so I surely need a cast somewhere… or just use another type that could be compatible with cocos2dx then ?


#13

how about use JniHelper::jstring2string() ?
It converts a jstring to an std::string.
see the following code…

JniHelper::getMethodInfo(t, “com/domain/Class”, “getSomeString”, “()Ljava/lang/String;”);
jstring retJstring = (jstring)t.env->CallObjectMethod(t.classID, t.methodID);
string str = JniHelper::jstring2string(retJstring);


#14

Thanks, didn’t notice this function :stuck_out_tongue:

Ok so at this point I try to use my C++ DeviceDetection class in the main.cpp to make something like this :

DeviceDetection* myInfo = DeviceDetection::sharedDeviceDetection(); //get the shared instance

And then, just to check it :

LOGD("info : %s", myInfo->deviceSystemName());

Eclipse always prompt me a “undefined reference to `DeviceDetection::sharedDeviceDetection()’”
It’s define like this in my .f file :

static DeviceDetection* sharedDeviceDetection();
DeviceDetection* pDeviceDetection;

and in the .cpp :

static DeviceDetection* DeviceDetection::sharedDeviceDetection()
{
if (! pDeviceDetection)
{
pDeviceDetection = new DeviceDetection();
}

return pDeviceDetection;
}

Did you see what I’m missing again ? :confused:
(full .h and .cpp files given in previous message)


#15

Hi Kevin,

what do you mean for “.f file”?

No doubt that you defined sharedDeviceDetection() as a public function, right?

I think it weired to operate a non-static object in a static function.
You might define pDeviceDetection as a static object, or it makes no sense in this case. However I don’t know whether it could solve the problem.

BTW, considering below code:
DeviceDetection* myInfo = DeviceDetection::sharedDeviceDetection;
myInfo->pDeviceDetection = 0;

I would like to say it’s not recommended to create a pointer to store a singleton object. Just use DeviceDetection::sharedDeviceDetection->pDeviceDetection when using it.


#16

Hi Kevin,

I just reviewed the code again, here’s a question when you using myInfo object:

DeviceDetection::sharedDeviceDetection() returns pDeviceDetection, so DeviceDetection::sharedDeviceDetection()>pDeviceDetection just like a pDeviceDetection>pDeviceDetection. I don’t know what it means.


#17

Effectively, I forgot to remove the line with the pDeviceDetection declaration, it’s a non-sense here of course…
And the .f was a mistake, it’s my .h header file, sorry…

I will follow you’re advice and remove the pointer I used before, so with things cleaner, I have now :
The DeviceDetection header file with the class definition :

using namespace cocos2d;
#include “Export.h”

class EXPORT_DLL DeviceDetection
{
public:
DeviceDetection();
DeviceDetection;
\ static\ DeviceDetection*\ sharedDeviceDetection;
\ static\ void\ end;
\ CCString\ deviceSystemName;
};
—————————————
The\ DeviceDetection\ cpp\ implementation\ file\ :

#include\ “DeviceDetection.h”
#include\ “DeviceDetectionJNI.h”
DeviceDetection::DeviceDetection
{
}
DeviceDetecton::DeviceDetection()
{

}

static DeviceDetection* DeviceDetection::sharedDeviceDetection()
{
if (! pDeviceDetection)
{
pDeviceDetection = new DeviceDetection();
}

return pDeviceDetection;
}

String* DeviceDetection::deviceSystemName()
{
return deviceSystemNameJNI();
}

void DeviceDetection::end()
{
endJNI();
}


And I just put my log command here in the cpp :


void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h)
{
if (!CCDirector::sharedDirector()->getOpenGLView())
{
CCEGLView view = &CCEGLView::sharedOpenGLView;
view->setFrameSize;
AppDelegate
pAppDelegate = new AppDelegate();
CCApplication::sharedApplication().run();
}
else
{
ccDrawInit();
ccGLInvalidateStateCache();

CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
CCTextureCache::reloadAllTextures();
CCNotificationCenter::sharedNotificationCenter()->postNotification(EVNET_COME_TO_FOREGROUND, NULL);
CCDirector::sharedDirector()>setGLDefaultValues;
}
//DEBUG test//
LOGD (“info : %s”, DeviceDetection::sharedDeviceDetection()
>deviceSystemName());
}

Is this seems good for you ?
The remaining problem is my LOGD line as Eclipse throw an undefined reference on “DeviceDetection::sharedDeviceDetection()”


#18

Kevin Blanc wrote:

Is this seems good for you ?
The remaining problem is my LOGD line as Eclipse throw an undefined reference on “DeviceDetection::sharedDeviceDetection()”

there’s still some mismatch in posted code. however i could point some problems:

  1. I can’t find the definition of pDeviceDetection, which should be in .cpp files like below:
    static DeviceDetection* pDeviceDetection;

  2. the function sharedDeviceDetection() should not declared as static in .cpp file, as it’s already declared in .h file.

  3. the return type of function deviceSystemName() does not match in both .cpp and .h file, one of them is String* while another is CCString.

I’m not sure whether your error is happend when compiling it. I tried to include this file in my project, and after I corrected the above points it passed compiling.


#19

Has anyone come up with a working example of something like the above? Really all I want is an example from the Java side all the way to the C++ side of getting the android.os.Build.MODEL string.

I am sure that a simple example of ALL the pieces for something this straight forward would help a lot of people.