jni入门

基础

        装ndk,并在studio中设定ndk的位置。并在project下的gradle.propreties文件中添加上android.useDeprecatedNdk=true。假设当前的module名为deaml,则在demo的build.gradle文件中的defaultConfig区域块中添加如下信息:

        ndk {
            moduleName "NdkJniDemo"          //生成的so名字
            abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无}

函数签名

        参考
        在ndk中经常会用到类似于java中函数反射的方法(GetMethodID获取指定class文件中的某个函数的id),此时就需要一个参数能唯一标识某个函数,这个参数就是函数签名。
        一个函数签名由两部分组成:()以及()外面的部分。()中表示该方法的参数,外面部分表示该方法的返回值。每一种数据类型都有其对应的字符串,将相应的字符串拼接好即可。

基本数据类型


可以发现除了boolean对应Z,long对应J外,其余的都是首字母大写。void对应的为V。

引用数据类型

        由三部分组成:"L+全名+;"。如String的签名为:Ljava/lang/String;。注意:要用/将全名分开。

数组

        由两部分组成:[+数组元素类型对应的字符串。几维数组前面就加几个[。如boolean[]对应的是[B。

生成头文件及运行

        使用javah命令生成相应的头文件(javah为java bin目录下的文件,与javac处于同一目录中,因此需要将bin目录配置到环境变量path中)。其余步骤如下:

        第一步:定义好native方法。如下:

public class JNIInterface {
    public static native String getStringFromNative();
    static {//加载生成的.so文件,与上面配置在build.gradle中的ndk下的moduleName值要一致
        System.loadLibrary("NdkJniDemo");
    }
}
        然后make project,是为了在demo的/build/intermediates文件夹下生成classes文件夹。

        第二步:使用命令行,进入到demo/build/intermediates/classes/debug文件夹下。执行javah -jni命令,后面跟的参数为第一步定义好的类的全名。该步用来生成相应的头文件。如下:


        在该步中,也可以在studio的terminal中执行,但有可能会遇到提示javah不是内部命令之类的错误,并且环境变量已经配置完成,此时就需要在命令行中单独执行javah -jni命令。

        头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_baigle_demo_JNIInterface */

#ifndef _Included_com_baigle_demo_JNIInterface
#define _Included_com_baigle_demo_JNIInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_baigle_demo_JNIInterface
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_baigle_demo_JNIInterface_getStringFromNative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

        在第二行中引入了系统生成的jni.h头文件,并生成了相应的native对应的方法(Java_com_baigle_demo_JNIInterface_getStringFromNative)。只是该方法没有方法体,类似于java中的抽象方法。

        上述方法中,有两个参数,使用第一个参数可以调用jni.h(系统生成的一个头文件)中的方法,第二个jobject表示当前native方法所在的类的对象

        第二步补充:javah -jni生成的native方法中不含有参数,如果含有参数,生成方法如下:

native方法代码:

public class JNIUtils {
    public static native String getSign(PackageManager manager);
}
命令行如下:


        如果提示自己的native文件没找到,就重新make project一次。并且javah -classpath之间的分隔使用的是";[空格]"

        第三步:在demo/src/main中新建jni文件夹,并将第二步中生成的.h文件(在demo/build/intermediates/classes/debug文件夹下)剪切到该文件夹中。新建一个c文件,引入第二步生成的头文件。如下:

#include "com_baigle_demo_JNIInterface.h"
JNIEXPORT jstring JNICALL Java_com_baigle_demo_JNIInterface_getStringFromNative
        (JNIEnv * env, jobject obj){
        return (*env)->NewStringUTF(env,"12345678");
}
        第四步:make project,然后运行即可。

运行.so文件

        打开demo\build\intermediates\ndk\debug\lib文件夹,可以看到自己配置的三个体系结构下的.so文件。删除main/jni文件夹,然后将三个.so文件复制到libs目录中,并在build.gradle中添加如下代码
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
可以一样运行。

输出日志

        使用eclipse运行ndk时,需要配置Android.mk文件,但studio中不用。studio的Android.mk为自动生成,在build\intermediates\ndk\debug目录中。一个eclipse下的Android.mk如下(来自于ndk demo):

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := plasma
LOCAL_SRC_FILES := plasma.c
LOCAL_LDLIBS    := -lm -llog -ljnigraphics

include $(BUILD_SHARED_LIBRARY)

        其中LOCAL_LDLIBS中有值"-llog",则表示可以在c/c++文件中输出日志。而studio中Android.mk的一些内容可以在build.gradle文件中ndk{}块中进行配置——如LOCAL_MODULE便是ndk中配置的moduleName的值。所以,只需要在ndk{}中加入ldLibs "log"即可。具体步骤如下:

        第一,在ndk{}中添加ldLibs “log",完整的ndk{}如下:

        ndk {
            moduleName "ImageJniUtils"          //生成的so名字
            ldLibs "log","jnigraphics"          //可以使用log与graphics
            abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无
        }
        第二,在c/c++文件中引入android/log.h头文件。并且可以定义成宏函数。如下:
#include <android/log.h>

#define  LOG_TAG    "libplasma"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

        其中__android_log_print为android/log.h中定义的函数。

        第一个参数表示log的等级(如info,warn,error等),第二个参数表示输出日志的TAG(即Log.e(TAG,msg)中的TAG),第三个及以后的参数表示要输出的log(有可能在输出的时候会进行格式化)。使用如下:

        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);//格式化后输出
		LOGE("Bitmap format is not RGB_565 !");//直接输出

debug

        第一步:在build.gradle中的buildTypes{}中添加如下:
        debug {
            jniDebuggable true
        }

        第二步(参考):打开edit configurations,在弹出的对话框中选择android native。如下:

        第三步,配置android native。如下:


        其中在General选择卡中要选择一个要调试的module。

        第四步:在第三步的对话框中,选择Native Debugger选项卡,并添加一个Symbol directories,在要调试的module下的build文件夹中选择。如下:

        第五步:在运行时选择第三步填写的name,如上面的name为demo-native,因此在运行时选择demo-native即可。然后在代码中加断点跑debug即可。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值