cocos2d-x 中JniHelper類的使用

原文:http://blog.csdn.net/luxiaoyu_sdc/article/details/15874505

主體思路

通過JNI獲取java虛擬機,再獲取當前程序的JNI環境,通過JNI環境獲取需要調用的java類信息,再獲取需要調用的java類中的函數信息。再通過JNI環境調用,使用類信息、函數信息,調用對應的java函數。
看起來好像有點複雜,but不用擔心,cocos2d-x中有一個JniHelper類(頭文件的copyright爲:cocos2d-x.org,是Google提供的還是cocos2d-x小組自己封裝的我就不清楚了),它已經把這些工作封裝好了。

JniHelper類的使用

加入如下頭文件:

#include "platform/android/jni/JniHelper.h"

需要使用的接口如下:

static bool getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode);
static bool getMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode);

實現上我們只需要使用上面這兩個接口,就可以獲取java類的所有函數信息了。JNI環境的獲取、各種錯誤處理都已經在這兩個接口實現中封裝好了。
先上代碼,再來依次講解每個參數的意義和使用方法:

    //函數信息結構體
    JniMethodInfo minfo;
    bool isHave = JniHelper::getStaticMethodInfo(minfo,/*JniMethodInfo的引用*/
                                                 "com/omega/MyApp",/*類的路徑*/
                                                 "getJavaActivity",/*函數名*/
                                                 "()Ljava/lang/Object;");/*函數類型簡寫*/
    jobject activityObj;
    if (isHave)
    {
        //CallStaticObjectMethod調用java函數,並把返回值賦值給activityObj
        activityObj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);
    }

OK,很簡單。上面的代碼,就是使用JNI在C++中調用java類靜態函數的典型使用方法。只有兩步:

  • 1. 獲取java函數的信息,classid、methodid等等
  • 2. 選擇JNIEnv中的接口,進行函數調用

getStaticMethodInfo參數詳解

兩個接口的參數一樣,意義也相同,詳解如下:
JniMethodInfo &methodinfo JniMethodInfo對象的引用,函數執行中會把jniEvn、classid、methodid寫入到引用中。
const char *className 類的路徑,把類的完整包名寫全,用法如以上代碼。
const char *methodName 函數名,函數名寫上就行了。

const char *paramCode 函數類型簡寫
這個參數需要單獨介紹,它的格式爲:(參數)返回類型。
例如:無參數,void返回類型函數,其簡寫爲 ()V
java中的類型對應的簡寫如下:

參數類型 參數簡寫
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
Object Ljava/lang/String; L用/分割類的完整路徑
Array [Ljava/lang/String; [簽名 [I

多參數的函數
如果函數有多個參數,直接把簡寫並列即可。注意Object與Array型參數簡寫結尾的分號,示例:
IIII //4個int型參數的函數
ILjava/lang/String;I //整形,string類型,整形組合 (int x, String a, int y)

通過JNIEnv進行函數調用

JNIEvn有一系列的CallStatic[返回類型]Method、Call[返回類型]Method接口,需要針對不同的函數返回類型選擇調用。
[返回類型]以函數返回類型的不同,對應不同的函數名。
例如:
CallStaticVoidMethod ———void
CallVoidMethod ———void
其對應關係如下:

函數名 函數返回值類型
Void void
Object jobject
Boolean jboolean
Byte jbyte
Char jchar
Short jshort
Int jint
Long jlong
Float jfloat
Double jdouble

參數傳遞
調用有參數的java函數時,需要把對應的參數傳遞進去。需要把參數按順序加入到classid、methodid後面,並且需要做類型轉換。例如:

jint jX = 10;
jint jY = 10;
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jX, jY);

參數類型轉換關係如下:

C++類型 JAVA類型
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
Object jobject
Class jclass
String jstring
Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray

string類型的轉換
實際上我們最常用的參數類型,主要是內建的數據類型、string字符串類型。數據類型可以直接轉爲j類型,但是string類型需要做如下處理:

jstring jmsg = minfo.env->NewStringUTF("http://www.baidu.com");
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jmsg);

非靜態函數的調用

非靜態函數的調用與靜態函數的調用類型,但是需要通過一個靜態函數獲取java類對象。
示例:

//C++代碼
    //1. 獲取activity靜態對象
    JniMethodInfo minfo;
    bool isHave = JniHelper::getStaticMethodInfo(minfo,
                                                 "com/omega/MyApp",
                                                 "getJavaActivity",
                                                 "()Ljava/lang/Object;");
    jobject activityObj;
    if (isHave)
    {
        //調用靜態函數getJavaActivity,獲取java類對象。
        activityObj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);
    }

    //2. 查找displayWebView接口,獲取其函數信息,並用jobj調用
    isHave = JniHelper::getMethodInfo(minfo,"com/omega/MyApp","displayWebView", "(IIII)V"); 

    if (!isHave)
    {
        CCLog("jni:displayWebView 函數不存在");
    }
    else
    {
        //調用此函數
        jint jX = (int)tlX;
        jint jY = (int)tlY;
        jint jWidth = (int)webWidth;
        jint jHeight = (int)webHeight;
        //調用displayWebView函數,並傳入參數
        minfo.env->CallVoidMethod(activityObj, minfo.methodID, jX, jY, jWidth, jHeight);
    }

詳盡的示例代碼

最後,放一塊比較詳細的JNI使用代碼,基本上覆蓋了的全部使用情況。

    JniMethodInfo minfo;//JniHelper   

    /* 測試用方法 */ 
    /*bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/cocoa/HiWorld","loginGree", "()V"); //
     if (isHave) {
     //CCLog("有showText ");
     minfo.env -> CallStaticVoidMethod(minfo.classID,minfo.methodID);
     }else
     {
     //CCLog("沒有方法showText");
     }*/

    /* 分享 */
    /*//將c++中的string轉換成java中的string
     //char str[] = "test";
     bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/cocoa/HiWorld","shareSina", "(Ljava/lang/String;Ljava/lang/String;)V"); //
     if (isHave) {
     //CCLog("有share ");
     jstring jstr = minfo.env->NewStringUTF("test1 share");
     jstring jst = minfo.env->NewStringUTF("/data/data/com.cocoa/cy.png"); 
     //jstring jst = minfo.env->NewStringUTF("");
     minfo.env -> CallStaticVoidMethod(minfo.classID,minfo.methodID,jstr,jst);
     }else
     {
     //CCLog("沒有方法share");
     }*/
    /* 設置高分 */
    /*jint ind = 0;
     jlong lsre = 2202l;
     bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/cocoa/HiWorld","setHighScore", "(IJ)V"); 
     if (isHave) {
     minfo.env -> CallStaticVoidMethod(minfo.classID,minfo.methodID,ind,lsre);            
     }*/
    /* 成就解鎖 */
    /*jint aind = 0;
     bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/cocoa/HiWorld","unLock", "(I)V"); 
     if (isHave) {
     minfo.env -> CallStaticVoidMethod(minfo.classID,minfo.methodID,aind);            
     }*/
    /* 測試用方法 */ 
    bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/cocoa/HiWorld","rtnActivity","()Ljava/lang/Object;");
    jobject jobj;
    if (isHave) { 
        jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID); 
    }       
    //CCLog(" jobj存在"); 
    /* 測試用方法,非靜態無參數無返回值方法 */
    /*isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","showText", "()V"); 
     if (isHave) {
     minfo.env -> CallVoidMethod(jobj,minfo.methodID);
     }*/
    /* 測試用方法,非靜態有java類型的String參數無返回值方法 */
    /*isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","showText", "(Ljava/lang/String;)V"); 
     if (isHave) {
     jstring jmsg = minfo.env->NewStringUTF("msg okey!");
     minfo.env -> CallVoidMethod(jobj,minfo.methodID,jmsg);
     }*/
    /* 測試用方法,返回java類型的String,有java類型的String和int參數方法 */
    /*isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","showText", "(Ljava/lang/String;I)Ljava/lang/String;"); 
     if (isHave) {
     jstring jmsg = minfo.env->NewStringUTF("msg okey! return string");
     jint index = 0;
     minfo.env -> CallObjectMethod(jobj,minfo.methodID,jmsg,index);
     }*/
    /* 測試用方法,返回java類型的String[],有java類型的String[]和int參數方法 */
    /*isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","showText", "([Ljava/lang/String;I)[Ljava/lang/String;"); 
     if (isHave) {
     jobjectArray args = 0;
     jstring str;
     jsize len = 5;
     const char* sa[] = {"Hi,","World!","JNI ","is ","fun"};
     int i = 0;
     args = minfo.env->NewObjectArray(len,minfo.env->FindClass("java/lang/String"),0);
     for(i=0;iNewStringUTF(sa[i]);
     minfo.env->SetObjectArrayElement(args,i,str);
     }
     //minfo.env->GetStringArrayRegion(args,0,10,buf);
     //jintArray jmsg = {1,2,3};
     //minfo.env->NewStringUTF("msg okey! return string");
     jint index = 0;
     minfo.env -> CallObjectMethod(jobj,minfo.methodID,args,index);
     }*/
    /* 測試用方法,無返回類型,有java類型的int[]和int參數方法 */
    /*isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","testArr", "([II)V"); 
     if (isHave) {
     jint buf[]={7,5,8,9,3};
     jintArray jintArr; //定義jint數組
     jintArr = minfo.env->NewIntArray(5);
     minfo.env->SetIntArrayRegion(jintArr,0,5,buf);
     jint index = 0;
     minfo.env -> CallVoidMethod(jobj,minfo.methodID,jintArr,index);
     }*/
    /* 測試用方法,無返回類型,有java類型的byte[]和int參數方法 */
    isHave = JniHelper::getMethodInfo(minfo,"com/cocoa/HiWorld","testArr", "([BI)V"); 
    if (isHave) {
        jbyte buf[]={7,5,8,9,3};
        jbyteArray jbyteArr; //定義jbyte數組
        jbyteArr = minfo.env->NewByteArray(5);
        minfo.env->SetByteArrayRegion(jbyteArr,0,5,buf);
        jint index = 0;
        minfo.env -> CallVoidMethod(jobj,minfo.methodID,jbyteArr,index);
    }
private static HiWorld hiWorld = null;
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    hiWorld = this;
    if (detectOpenGLES20()) {
        // get the packageName,it's used to set the resource path
        String packageName = getApplication().getPackageName();
        super.setPackageName(packageName);
        // set content
        setContentView(R.layout.game_demo);
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
                                  R.layout.window_title);

        mGLView = (Cocos2dxGLSurfaceView) findViewById(R.id.game_gl_surfaceview);
        mGLView.setTextField((Cocos2dxEditText) findViewById(R.id.textField));
        mGLView.setEGLContextClientVersion(2);
        mGLView.setCocos2dxRenderer(new Cocos2dxRenderer());
        task = new TimerTask() {
            @Override
            public void run() {
                // HiWorld.shoot(hiWorld);
                Log.e("-------------------", "-------------------");
                // 調用c++中的方法
                System.out.println("------------------------"
                                   + stringZjy1());
            }
        };
        timer = new Timer();
        timer.schedule(task, 5000);
    } else {
        Log.d("activity", "don't support gles2.0");
        finish();
    }

    static {
        System.loadLibrary("game");
    }

    // c++中調用的方法
    public static Object rtnActivity() {
        System.out.println("----------rtnActivity");
        return hiWorld;
    }

    // c++中調用的方法,傳String類型
    public void showText(final String msg) { 
        // 添加到主線程
        hiWorld.runOnUiThread(new Runnable() {
            public void run() { 
                System.out.println("----------msg:"+msg);
            }
        });
    }
    //c++中調用的方法,傳String類型和int類型
    public String showText(final String msg,final int index) { 
        // 添加到主線程
        hiWorld.runOnUiThread(new Runnable() {
            public void run() { 
                System.out.println("----------msg:"+msg+"; index="+index);
            }
        });
        return "okey String showText(final String msg,final int index)";
    }
    //c++中調用的方法,傳String[]類型和int類型
    public String[] showText(final String[] msg,final int index) { 
        String[] strArr = {"1","2","3","4","5"};
        // 添加到主線程
        hiWorld.runOnUiThread(new Runnable() {
            public void run() { 
                for(String _str:msg){
                    System.out.println("----------String[] msg:"+_str+"; index="+index);
                }
            }
        });
        return strArr;
    }
    //c++中調用的方法,傳int[]類型和int類型
    public void testArr(final int msg[],final int index) { 
        // 添加到主線程
        hiWorld.runOnUiThread(new Runnable() {
            public void run() { 
                System.out.println("----------int[] msg len:"+msg.length);
                for(int _bl:msg){
                    System.out.println("----------int[] msg:"+_bl+"; index="+index);
                }
            }
        });
    }
    //c++中調用的方法,傳int[]類型和int類型
    public void testArr(final byte msg[],final int index) { 
        // 添加到主線程
        hiWorld.runOnUiThread(new Runnable() {
            public void run() { 
                System.out.println("----------byte[] msg len:"+msg.length);
                for(int _bl:msg){
                    System.out.println("----------byte[] msg:"+_bl+"; index="+index);
                }
            }
        });
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章