之前写了第一个JNI程序,有翻译了一篇NDK的入门教程,今天从头到尾自己写了一个。
环境:linux+ndk r6
开始之前:java和ndk的环境变量已经设置好
目标:一个简单的例子,从c中传给java int数组和字符串数组
开始:
新建一个项目,名称为FirstNDK
一个包是package com.linc.FirstNDK;放activity
另一个是package com.linc.lib;放lib文件,主要处理jni
布局:两个按钮+一个textview
package com.linc.lib包下新建一个类FirstLib
package com.linc.lib;
public class FirstLib {
// Native implementation
static {
System.loadLibrary("FirstLib");
}
//传递数组
public static native int[] intMethod();
//传递字符串数组
public static native String[] stringMethod();
}
这里主要有两个本地方法,
下面用它生成jni的头文件:用终端来到项目bin目录(前提是FirstLib这个类已经编译完成),
[linc@localhost bin]$ javah -jni com.linc.lib.FirstLib
项目中新建一个jni目录,用来存放jni相关文件,并把刚刚生成的头文件放进去
[linc@localhost bin]$ mv com_linc_lib_FirstLib.h ../jni
在jni中新建一个c文件与上面的对应叫FirstLib.c,
#include "com_linc_lib_FirstLib.h"
const static int length=10;
//传递整型数组
JNIEXPORT jintArray JNICALL Java_com_linc_lib_FirstLib_intMethod
(JNIEnv *env, jclass obj)
{
jintArray array;//数组
array=(*env)->NewIntArray(env,10);
int i=1;
for(;i<=10;++i)
{
(*env)->SetIntArrayRegion(env,array,i-1,1,&i);
}
//获取数组长度
int len=(*env)->GetArrayLength(env,array);
//获得数组中的所有元素
jint* elems=(*env)->GetIntArrayElements(env,array,0);
return array;
}
//字符串数组
JNIEXPORT jobjectArray JNICALL Java_com_linc_lib_FirstLib_stringMethod
(JNIEnv *env, jclass obj)
{
jclass class=(*env)->FindClass(env,"java/lang/String");
jobjectArray string=(*env)->NewObjectArray(env,(jsize)length,
class,0);
jstring jstr;
char* _char[]={"my ","name ","is ",
"linc!!","正在","学习",
"JNI","和","NDK","技术!"
};
int i=0;
for(;iNewStringUTF(env,_char[i]);
(*env)->SetObjectArrayElement(env,string,i,jstr);
}
return string;
}
在jni目录下新建一个mk文件,Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := FirstLib
LOCAL_SRC_FILES := FirstLib.c
include $(BUILD_SHARED_LIBRARY)
[linc@localhost FirstNDK]$ cd ..
[linc@localhost FirstNDK]$ ndk-build
Compile thumb : FirstLib <= FirstLib.c
SharedLibrary : libFirstLib.so
Install : libFirstLib.so => libs/armeabi/libFirstLib.so
package com.linc.FirstNDK;
import java.io.UnsupportedEncodingException;
import com.linc.lib.FirstLib;
import com.linc.lib.PersonalInfo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class FirstNDK extends Activity {
TextView textResult;
Button buttonOne;
Button buttonTwo;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textResult = (TextView) findViewById(R.id.Result);
buttonOne = (Button) findViewById(R.id.btnOne);
buttonOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int[] nums=FirstLib.intMethod();
for(int i=0;i
最后就可以运行看效果了!
希望大家用ndk来开发游戏时,玩的开心!
后记:
1.ndk 未找到。。。类的错误。
今天发现这个错误,主要是因为项目bin路径下还有一层classes。如下处理就可以了:
~/workspace/TestNDK/bin$ javah -classpath ./classes -d ./jni com.linc.lib.FirstLib
2.loadlibrary相关
Android.mk 可以这样写(摘自cocos2d-x中的demo):
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := game_shared
LOCAL_MODULE_FILENAME := libgame
LOCAL_SRC_FILES := hellocpp/main.cpp \
../../Classes/AppDelegate.cpp \
../../Classes/HelloWorldScene.cpp \
../../Classes/GameOverScene.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes
LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static cocosdenshion_static
include $(BUILD_SHARED_LIBRARY)
$(call import-module,CocosDenshion/android)
$(call import-module,cocos2dx)
LOCAL_MODULE 是game_shared,如果没有定义LOCAL_MODULE_FILENAME,那么编译出来的so就是libgame_shared.so,
而在java文件中load时就必须要
static {
System.loadLibrary("game_shared");
}
有趣的是,定义LOCAL_MODULE_FILENAME为libgame后,编译出来的so当然是libgame.so了,而java中load “game”就可以了。
3.NDK r7之后直接对编译的支持
在整理NDK r7的特性时,直接提供对编译的支持,在windows下就不用cygwin了。
现在我已经使用r9b了,项目右键--->Android Tools--->Add Native Support...
你会发现帮你添加了jni的文件夹,并且项目属性中在Builders里面有多了两项,一个是CDT Builder,一个是Scanner Configuration Builder。
更贴心的,属性都为你设置好了,直接添加编辑你的C、C++代码吧。