c知识
stdlib 头文件即standard library标准库头文件 常用系统函数,跟系统调用相关的,比如内存申请malloc和释放free
stdio是标准io函数,比如printf和scanf函数
windows和linux文件区别
windows .exe .dll .bat
linux .elf .so .sh
x86对 jni兼容性能很差
由于ndk一开始是做给linux下用的,所有wind下用ndk会有很多问题。
所以还要装个软件 cygwin
只要装2个功能 devel shells
ndk环境变量设置
windows下 1.直接解压缩ndk,然后搭建环境变量. 在path目录下面C:\android-ndk-r7b . 这是直接在cmd命令行下运行 ndk-build
Cygwin Terminal下 这个配置环境变量在 cygwin/etc/profile的32行
/cygdrive/c/android-ndk-r7b 把这个添加 不同ndk的安装路径不一样
每个环境变量用:分隔.
在cygwin下配置了环境便利ndk但是在别的目录 运行ndk-build一直找不到目录 .
执行./ndk-build -C samples/hello-jni 这个代码可解决 博客:http://blog.sina.com.cn/s/blog_4c73bcc80101177e.html
Jni.h 目录:C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\include
log.h目录:C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\include\android
实现步骤
1 定义一个c方法的接口 相当于在java代码中定义了一个接口 接口的实现方法是C语言实现的
2 步 实现C代码
3步骤 创建android.mk 告诉编译器 如何把c代码打包成函数库
4步 把c代码 打包成函数库 用到了安装的环境 通过cygwin terminal
5 步在java代码中 引入库函数
static{
System.loadLibrary("hello");// 注意事项 去掉前面的lib 后面的.so
}
6 使用方法
Anroid.mk 文件
LOCAL_PATH := $(call my-dir) // 返回当前c代码目录
include $(CLEAR_VARS) // 清楚了所有 已local 开头的配置文件 唯独不清楚LOCAL_PATH
LOCAL_MODULE := hello // 库函数的名字 严格遵守makefile 格式 lib .so 如果前面加lib 不会自动生成了
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY) // 加入库函数
jni 常见的错误
1错误1 忘记方法的参数
2 错误2 203-28 03:41:56.758: E/AndroidRuntime(821): java.lang.UnsatisfiedLinkError: Native method not found: com.example.error.DemoActivity.helloWorld:()Ljava/lang/String; 方法名错误
3 错误3 一般没有日志打印 直接报错工程停止 一般c代码有运行错误
4 错误4 在交叉编译的工具链上报错 c代码有编译错误 比如 一些函数没有声明 一些类型没有声明 少符号
5 错误5 没有Android.mk 文件
6 错误6 Android.mk 文件有错
7 错误7 引用别人.so 函数库 需要你自己native方法对应类的包名 和之前打包成.so函数库的包名一致
使用javah时 有时一直报错:找不到类文件 要添加环境变量classpath
把安卓adt adt\adt\adt-bundle-windows-x86_64-20140702\sdk\platforms\android-10
这个目录下的android.jar 解压出来.然后把这个目录放在classpath里面加入
例: classpath:.;D:\Program Files\Android\android-sdk\platforms\android-8\android
然后到src目录下 javah 包名.类名. 如果报错找不到类文件就到 bin/classes下
获取方法签名:
使用javap -s 获取内部类型签名 这个在反射方法时候要用到
在bin/classes 下执行:javap -s 包名.类名字
1
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ )<br>解释: |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
可变参数宏 ...和__VA_ARGS_ _ __VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。 实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏_ _VA_ARGS_ _就可以被用在替换部分中,替换省略号所代表的字符串。比如: #define PR(...) printf(__VA_ARGS__) int main() { int wt=1,sp=2; PR( "hello\n" ); PR( "weight = %d, shipping = %d" ,wt,sp); return 0; } 输出结果: hello weight = 1, shipping = 2 省略号只能代替最后面的宏参数。 #define W(x,...,y)错误! |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
把java里的string转成c里面的 char * 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); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env,barr); jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if (alen > 0) { rtn = ( char *) malloc (alen+1); //"\0" memcpy (rtn,ba,alen); rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env,barr,ba,0); // return rtn; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
通过反射 调用java代码 原理一样 java里面的反射 Class<?> forName = Class.forName( "com.example.ndkcallback.DataProvider" ); Method declaredMethod = forName.getDeclaredMethod( "helloFromJava" , new Class[]{}); declaredMethod.invoke(forName.newInstance(), new Object[]{}); c里面反射java ///jclass (*FindClass)(JNIEnv*, const char*); jclass clazz=(*env)->FindClass(env, "com/example/ndkcallback/DataProvider" ); // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); // 方法签名 参数和返回值 jmethodID methodId=(*env)->GetMethodID(env,clazz, "helloFromJava" , "()V" ); // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,jobject,methodId); |
第一个:helloworld
创建jni目标.
创建Hello.c
#include<stdio.h> #include<jni.h> jstring Java_com_example_myhello_MainActivity_helloworldFromc(JNIEnv* env,jobject obj){ return (*env)->NewStringUTF(env,"onehello"); }
创建Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := Hello.c include $(BUILD_SHARED_LIBRARY)
然后activity里面加载library
package com.example.myhello; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { public native String helloworldFromc(); static{ System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View view) { // TODO Auto-generated method stub Toast.makeText(getApplicationContext(), helloworldFromc(), 0).show(); } }
c代码调用java代码事例
要调用的方法
1
2
3
4
5
6
7
|
package com.example.threehello; public class DataProvider { public native int add( int x, int y); public native String sayHello(String s); public native int [] intMethod( int []iNum); } |
在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
50
51
52
53
54
55
56
57
58
59
60
61
62
|
#include <stdio.h> #include <com_example_threehello_DataProvider.h> #include <android/log.h> //调用java代码log的时候导入这个头文件 #include <string.h> #define LOG_TAG "clog" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__ ) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ ) char * Jstring2CStr(JNIEnv* env, jstring jstr) //java里的string转 c的char* { 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); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env,barr); jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if (alen > 0) { rtn = ( char *) malloc (alen+1); //"\0" memcpy (rtn,ba,alen); rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env,barr,ba,0); // return rtn; } JNIEXPORT jint JNICALL Java_com_example_threehello_DataProvider_add (JNIEnv * env, jobject jobject, jint x, jint y){ LOGD( "x=%d" ,x); LOGI( "y=%d" ,y); return x+y; } JNIEXPORT jstring JNICALL Java_com_example_threehello_DataProvider_sayHello (JNIEnv * env, jobject jobject, jstring str){ //jstring NewStringUTF(const char* bytes) char *c= "hello" ; char *strs = Jstring2CStr(env,str); strcat (strs,c); LOGD( "%s" ,strs); return (*env)->NewStringUTF(env,strs); } JNIEXPORT jintArray JNICALL Java_com_example_threehello_DataProvider_intMethod (JNIEnv * env, jobject jobject, jintArray jarray){ // jsize (*GetArrayLength)(JNIEnv*, jarray); // jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); int length = (*env)->GetArrayLength(env,jarray); int *array = (*env)->GetIntArrayElements(env,jarray,0); int i=0; for (;i<length;i++){ *(array+i)+=5; } return jarray; } |
c代码调用java代码 log
先导入 头文件 : #include <android/log.h>
在 Android.mk 里面加入 LOCAL_LDLIBS += -llog 这个动态链接库在C:\android-ndk-r7b\platforms\android-14\arch-arm\usr\lib 目录下
c代码回调java代码
事例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.example.fourhello; public class DataProvider { public void helloFromJava(){ System.out.println( "haha我被调用了" ); } public int Add( int x, int y){ int result = x+y; System.out.println(result); return result; } public void printString(String s){ System.out.println(s); } public native void callMethod1(); public native void callMethod2(); public native void callMethod3(); } |
相当于java的反射
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
|
#include<stdio.h> #include<string.h> #include<com_example_fourhello_DataProvider.h> JNIEXPORT void JNICALL Java_com_example_fourhello_DataProvider_callMethod1 (JNIEnv * env, jobject jobject){ //jclass (*FindClass)(JNIEnv*, const char*); // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" ); jmethodID methodID = (*env)->GetMethodID(env,clazz, "helloFromJava" , "()V" ); (*env)->CallVoidMethod(env,jobject,methodID); } JNIEXPORT void JNICALL Java_com_example_fourhello_DataProvider_callMethod2 (JNIEnv * env, jobject jobject){ jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" ); jmethodID methodID = (*env)->GetMethodID(env,clazz, "Add" , "(II)I" ); // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallIntMethod(env,jobject,methodID,3,5); } JNIEXPORT void JNICALL Java_com_example_fourhello_DataProvider_callMethod3 (JNIEnv * env, jobject jobject){ jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" ); jmethodID methodID = (*env)->GetMethodID(env,clazz, "printString" , "(Ljava/lang/String;)V" ); // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); //jstring (*NewStringUTF)(JNIEnv*, const char*); jstring str = (*env)->NewStringUTF(env, "i do call back" ); (*env)->CallVoidMethod(env,jobject,methodID,str); } |
c代码调用java 其他类里的方法
1
2
3
4
5
6
7
|
public class MainActivity extends Activity { public void hellocall(){ System.out.println( "main activity callback" ); } } |
1
2
3
4
5
6
7
8
|
JNIEXPORT void JNICALL Java_com_example_fourhello_DataProvider_callMethod4 (JNIEnv * env, jobject j){ jclass clazz = (*env)->FindClass(env, "com/example/fourhello/MainActivity" ); jmethodID methodID = (*env)->GetMethodID(env,clazz, "hellocall" , "()V" ); //jobject (*AllocObject)(JNIEnv*, jclass); 获取jobject对象 jobject jj = (*env)->AllocObject(env,clazz); (*env)->CallVoidMethod(env,jj,methodID); } |
c代码回调java的静态方法
1
2
3
|
public static void mystatic(){ System.out.println( "呵呵我是静态方法" ); } |
1
2
3
4
5
6
7
8
|
JNIEXPORT void JNICALL Java_com_example_fourhello_DataProvider_callMethod5 (JNIEnv * env, jobject jobject){ jclass clazz = (*env)->FindClass(env, "com/example/fourhello/DataProvider" ); // jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodID = (*env)->GetStaticMethodID(env,clazz, "mystatic" , "()V" ); // void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); (*env)->CallStaticVoidMethod(env,clazz,methodID); } |
jni在项目开发的用途 通过jni直接调用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
|
package com.example.fivehello; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { static { System.loadLibrary( "hello" ); } public native int login(String password); @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onclick(View view){ int login = login( "123" ); Toast.makeText(getApplicationContext(), login+ "" , 0 ).show(); } } |
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
|
#include<stdio.h> #include<com_example_fivehello_MainActivity.h> #include<string.h> int login( char * psw){ char * rightword = "123" ; int i = strcmp (rightword,psw); if (i==0){ return 200; } else { return 302; } } 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); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env,barr); jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if (alen > 0) { rtn = ( char *) malloc (alen+1); //"\0" memcpy (rtn,ba,alen); rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env,barr,ba,0); // return rtn; } JNIEXPORT jint JNICALL Java_com_example_fivehello_MainActivity_login (JNIEnv * env, jobject jobject, jstring str){ char *c = Jstring2CStr(env,str); return login(c); } |
原文:http://www.cnblogs.com/wikiki/p/4058048.html