zoukankan      html  css  js  c++  java
  • cocos2d-x中使用JNI的调用JAVA方法

    用cocos2d-x公布Android项目时。都应该知道要用JAVA与C/C++进行交互时会涉及到JNI的操作(Java Native Interface)。JNI是JAVA的一个通用接口。旨在本地化语言(如CC++)与JAVA语言进行交互。在交互过程成中,JAVA调用的是已编译好的本地化语言的二进制代码(如Windows下的dll。Linux下的so文件)。所以在交叉编译时看到Eclipse的控制台输出

    [armeabi] SharedLibrary  : libcocos2dcpp.so
    [armeabi] Install        : libcocos2dcpp.so => libs/armeabi/libcocos2dcpp.so

    时就可以觉得cocos2d-x项目在Android平台编译成功,其原理就是生成这个二进制文件(动态连接库)与Android的本地语言———JAVA进行交互,而交互的过程须要一个中间的桥梁。这个桥梁就是JNI。

    那么接下来就记录下怎样在cocos2d-x中用C++中使用JNI调用Java语言的中的成员方法或静态方法。

    在cocos2d-x中有个已封装好的用于方便操作操作JNI的类——JniHelper,主要通过下面两个函数来获取JAVA类的信息。

    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);
    顾名思义,第一个函数是获取静态方法的信息。第二个是获取成员方法的信息。其參数都是一致的各自是

    methodinfo:JniMethodInfo类的引用

    className:类的路径(即包名加类名)

    methodName:方法名

    paramCode:方法的參数类型和返回值类型

    当中的第四个參数的编写规则大致例如以下:

    (參数类型...)返回值类型。

    编写规则能够參照oracle的官方文档 http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html

    在c++数据类型映射到java数据类型时已下面的一个联合体方式来定义

    typedef union jvalue { 
        jboolean z; 
        jbyte    b; 
        jchar    c; 
        jshort   s; 
        jint     i; 
        jlong    j; 
        jfloat   f; 
        jdouble  d; 
        jobject  l; 
    } jvalue; 

    能够看到。这个union包括了映射JAVA的8个原声数据类型和一个自己定义类型。这样就囊括了JAVA全部的数据类型的映射。

    并且。JAVA VM(JAVA虚拟机)还定义了一下的签名来表明JAVA的数据类型

    Z    boolean

    B    byte

    C    char

    S    shot

    I    int

    J    long

    F    float

    D    double

    L fully-qualified-class;    fully-qualified-class(注意分号)

    [type    type[]

    (arg-types)ret-type    method type

    举个样例来说

    假设想调用一下的一个JAVA方法

    long f(int n, String s, int[] arr);

    那么第四个參数(paramCode)能够写为

    (ILjava/lang/String;[I)J

    当然,上面的命名规则也能够通过JAVA反编译来获取。


    从上图就能够看出一个类的全部方法签名。由此也解释了JAVA虚拟机在解析JAVA字节码时就是通过上面的方法签名来找到对应的方法入口地址。

    接下来来測试通过JniHelper来帮助实现c++调用一个带有參数和返回值的静态JAVA方法的过程

    1. 创建一个新的cocos2d-x项目。然后拿到Eclipse上去编译。编译完毕后编辑入口类,增加我们要測试的JAVA代码

    package com.cocos2dx.TestJNI;
    
    import org.cocos2dx.lib.Cocos2dxActivity;
    import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
    
    import android.os.Bundle;
    
    public class TestJNI extends Cocos2dxActivity{
    	
        protected void onCreate(Bundle savedInstanceState){
    		super.onCreate(savedInstanceState);	
    	}
    
        public Cocos2dxGLSurfaceView onCreateView() {
        	Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        	// TestJNI should create stencil buffer
        	glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);
        	
        	return glSurfaceView;
        }
    
        static {
            System.loadLibrary("cocos2dcpp");
        }     
        
        /////////////////////////增加JAVA測试代码/////////////////////////
      	public static String staticMethod(int i, String str, double d) {
      		return "i: " + i + ", str: " + str + ", d: " + d;
      	}
    }

    2. 改动HelloWorldScene.cpp,增加jni的调用代码

    #include "HelloWorldScene.h"
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    #include <jni.h>
    #include "platform/android/jni/JniHelper.h"
    #endif
    USING_NS_CC;
    
    CCScene* HelloWorld::scene() {
        CCScene *scene = CCScene::create();
        HelloWorld *layer = HelloWorld::create();
        scene->addChild(layer);
        return scene;
    }
    
    bool HelloWorld::init() {
        if (!CCLayer::init()) {
            return false;
        }
    	#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    	JniMethodInfo methodInfo;
    	//推断方法是否存在
    	bool isExist = JniHelper::getStaticMethodInfo(methodInfo, "com/cocos2dx/TestJNI/TestJNI", "staticMethod", "(ILjava/lang/String;D)Ljava/lang/String;");
    	if(isExist) {
    		jint i = 0;
    		jdouble d = 1.22;
    		jstring str = methodInfo.env->NewStringUTF("Test Static Method");//创建
    		jstring s = (jstring)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, i, str, d);//调用Java方法
    		const char* c = methodInfo.env->GetStringUTFChars(s, 0);//将jstring转成字符指针
    		//把返回的字符串显示在屏幕中
    		CCLabelTTF* ttf = CCLabelTTF::create(c, "Arial", 30);
    		ttf->setPosition(ccp(300, 160));
    		this->addChild(ttf);
    		//释放内存
    		methodInfo.env->ReleaseStringUTFChars(s, c);
    		methodInfo.env->DeleteLocalRef(str);
    	}else {
    		CCLog("share error");
    	}
    	#endif
        return true;
    }
    
    

    执行结果就能够看到调用成功:


  • 相关阅读:
    IOS RunLoop理解(参考YYKit)
    与个推人员的沟通
    idea报错:找不到或无法加载主类
    linux couldnt resolve host mirrors.aliyun.com解决方法
    【Linux】简单明了查看内存使用和ubuntu的版本号及位数
    【Linux】scp“免密” 远程copy较多文件
    【Repo】推送一个已有的代码到新的 gerrit 服务器
    Android之Monkey全参数(包含隐藏参数)
    Android系统adb命令查看CPU与内存使用率
    adb 命令连接指定设备
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5126141.html
Copyright © 2011-2022 走看看