之前写了第一个JNI程序,有翻译了一篇NDK的入门教程,今天从头到尾自己写了一个。
环境:linux+ndk r6
开始之前:java和ndk的环境变量已经设置好
目标:一个简单的例子,从c中传给java int数组和字符串数组
开始:
新建一个项目,名称为FirstNDK
一个包是package com.linc.FirstNDK;放activity
另一个是package com.linc.lib;放lib文件,主要处理jni
布局:两个按钮+一个textview
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/btnOne" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="int Array!" /> <TextView android:id="@+id/Result" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Result:" /> <Button android:id="@+id/btnTwo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="string Array!" /> </LinearLayout>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(;i<length;++i) { jstr=(*env)->NewStringUTF(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<nums.length;++i) { textResult.append(" "+nums[i]); } } }); buttonTwo = (Button) findViewById(R.id.btnTwo); buttonTwo.setOnClickListener(btnTwo_Click); } private OnClickListener btnTwo_Click = new OnClickListener() { public void onClick(View v) { String[] string=FirstLib.stringMethod(); for(int i=0;i<string.length;++i) { try { string[i]=new String(string[i].getBytes("gb2312"),"GBK"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } textResult.append(" "+string[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++代码吧。