0x00 基础知识
jni 是java native interface,java调用底层C代码所用到的技术
ndk 是Android方便jni编程所提供的工具集合
在windows 环境下,需要安装cygwin工具,方便对c代码进行编译 配置ndk运行环境 打开cygwin64\etc\profile文件,编辑其中的环境变量,添加ndk位置变量:
1 2 3 4 5 if [ ${CYGWIN_NOWINPATH-addwinpath} = "addwinpath" ] ; then PATH ="/usr/local/bin:/cygdrive/h/software/android_ndk_develop/windows/android-ndk-r10e:/usr/bin${PATH:+:${PATH} }" else PATH ="/usr/local/bin:/cygdrive/h/software/android_ndk_develop/windows/android-ndk-r10e:/usr/bin" fi
安装cygwin选择163的源 ,安装bash和make即可。需要更新也是用setup文件。 参考链接:http://pielot.org/2010/12/using-cygwin-with-the-android-ndk-on-windows/ http://mirrors.163.com/.help/cygwin.html http://blog.csdn.net/hu_shengyang/article/details/7828998
0x01 开始编程 1. 创建工程 java部分
新建一个空的Android App程序,添加几个按钮,用于测试调用ndk。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="JAVA与C交互 integer" android:id="@+id/button2" android:layout_alignTop="@+id/button" android:layout_toRightOf="@+id/button" android:layout_toEndOf="@+id/button" android:layout_marginLeft="60dp" android:layout_marginStart="60dp" android:onClick ="clickInteger" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="JAVA与C交互 string" android:id="@+id/button3" android:layout_below="@+id/button" android:layout_alignLeft="@+id/button" android:layout_alignStart="@+id/button" android:layout_marginTop="72dp" android:onClick ="clickString" />
1 2 3 4 5 6 7 8 9 public void clickInteger(View view ) { Toast . makeText(this ,dataProvider .add (1, 2) + "" ,Toast.LENGTH_LONG).show() ; } public void clickString(View view ) { Toast . makeText(this ,dataProvider .sayHello ("angel~~" ) ,Toast.LENGTH_LONG).show() ; }
2. 编写C代码
1 2 cd app \build \intermediates \classes \debug javah com .example .huanqi .myapplication .DataProvider
便可生成对应的头文件com_example_huanqi_myapplication_DataProvider.h
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 extern "C" {JNIEXPORT jint JNICALL Java_com_example_huanqi_myapplication_DataProvider_add (JNIEnv *, jobject, jint, jint); /* * Class: com_example_huanqi_myapplication_DataProvider * Method: sayHello * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_huanqi_myapplication_DataProvider_sayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus}
这里用到了jni.h
头文件进行基础编程和android/log.h
文件进行打印日志。这里的C代码需要通过jni中数据转换为java可识别的类型。
data_test.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <string .h> #include "com_example_huanqi_myapplication_DataProvider.h" #include "until.h" #include "log.h" JNIEXPORT jint JNICALL Java_com_example_huanqi_myapplication_DataProvider_add (JNIEnv * env , jobject object , jint x , jint y) { LOGD("x = %d" ,x ) ; LOGD("y = %d" ,y ) ; return x + y ; } JNIEXPORT jstring JNICALL Java_com_example_huanqi_myapplication_DataProvider_sayHello (JNIEnv * env, jobject object, jstring message) { char *msg = Jstring2CStr(env , message ) ; LOGD("message = %s" , msg ) ; strcat(msg , "Hello world" ); LOGD("new message = %s" , msg ) ; return (*env) -> NewStringUTF(env ,msg ) ; }
log.h 公用log打印函数
1 2 3 4 5 #include <android/log.h> #define LOG_TAG "System.out.c" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
until.h 公用数据转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> #include <jni.h> #ifndef MYAPPLICATION_UNTIL_H #define MYAPPLICATION_UNTIL_H #endif #ifdef __cplusplus extern "C" {#endif JNIEXPORT char * Jstring2CStr (JNIEnv* env, jstring jstr) ; #ifdef __cplusplus } #endif
until.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include "until.h" #include <malloc.h> char * Jstring2CStr(JNIEnv* env , jstring jstr ) { char * rtn = NULL; jclass clsstring = (*env)->FindClass(env ,"java/lang/String" ) ; jstring strencode = (*env)->NewStringUTF(env ,"GB2312" ) ; jmethodID mid = (*env)->GetMethodID(env ,clsstring , "getBytes" , "(Ljava/lang/String;)[B" ) ; jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env ,jstr ,mid ,strencode ) ; jsize alen = (*env)->GetArrayLength(env ,barr ) ; jbyte* ba = (*env)->GetByteArrayElements(env ,barr ,JNI_FALSE) ; if (alen > 0 ) { rtn = (char *)malloc(alen+1 ); memcpy(rtn,ba,alen); rtn[alen ] =0 ; } (*env)->ReleaseByteArrayElements(env ,barr ,ba ,0) ; return rtn; }
3. 编译C代码
Android.mk
1 2 3 4 5 6 7 8 9 10 11 LOCAL_PATH := $(call my-dir ) include $(CLEAR_VARS) LOCAL_MODULE := datatest LOCAL_SRC_FILES := data_test.c until.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
将C代码编译为可以在arm平台上运行的库文件。这里需要使用cygwin,cd到jni目录中,输入ndk-build
命令即可。
1 2 3 4 $ ndk-build [armeabi] Compile thumb : datatest <= data_test.c [armeabi] SharedLibrary : libdatatest.so [armeabi] Install : libdatatest.so => libs/armeabi/ libdatatest.so
android studio 对于ndk的支持还只是实验阶段,所以需要在gradle.properties
文件中添加:
1 android.useDeprecatedNdk =true
然后将so文件放在src/main/jniLibs/armeabi
目录下,即可被项目找到。
0x02 运行调试
在运行时,记得在使用的地方加载运行库:
1 2 3 4 static { System . loadLibrary("datatest" ) ; }
然后点击项目运行,可以测试运行得到:
配置好sdk路径之后,可以使用adb对设备进行调试,权限是root。
1 2 3 4 5 6 7 8 9 10 adb devices List of devices attached 192.168.56.101:5555 deviceadb shell root@vbox86p:/ # ls acct cache ...
像eclipse一样,在输出的日志中,进行过滤操作。这里配置标签为System.out.c
0x03 git版本控制
1 2 3 4 5 git init git add README.md git commit -m “first commit” git remote add origin git@github.com:angelwhu/android_ndk.git git push -u origin master
可能需要登录github,先创建android ndk
仓库。
接着配置Android Studio Git 插件
http://blog.csdn.net/hello0370/article/details/41899207