jni

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);
}
posted @ 2014-10-28 21:53  wikiki  阅读(367)  评论(0编辑  收藏  举报