JNI简单尝试

这里主要是写一个实例来解释JNI的使用。

设置NDK路径

选择File–Project Structure–SDK Location

NDK

或者也可以通过直接修改local.properties,在里面指定NDK的所在目录。两种方法都是一样的。

NDK

配置ndk属性

打开app下的build.gradle文件,在defaultConfig节点下增加属性配置

ndk{
    moduleName "JniTest"
    ldLibs "log","z","m"
    abiFilters "armeabi","armeabi-v7a","x86"
}

以上配置代码指定的so库名称为JniTest,链接时使用到的库,对应android.mk文件中的LOCAL_LDLIBS,及最终输出指定三种abi体系结构下的so库。

配置gradle.properties文件

打开该文件,添加如下代码即可

android.useDeprecatedNdk=true

这里写图片描述

配置好,下面就可以开始写代码啦!

编写代码

建议:不在Activity中声明native方法,一是为了设计上的简洁并功能分离,二是创建头文件时避免不要的麻烦。

这里新建一个JniUtil类,声明一个native方法。

public class JniUtil {
    static {
        System.loadLibrary("JniTest");//加载so库
    }

    public native String getStringFromNative();//返回一句String
    public native int add(int a, int b);//传int到C后两数相加
}

编写MainActivity文件,这里只有两个Button按键,一个点击后调用显示一个Toast显示从JNI传过来的文字。另一个点击后两个数字相加后返回显示。加载so库System.loadLibrary(“JniTest”);也可以不写在JniUtil上,写在MainActivity上,一样的。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_jni, btn_jni_int;
    private JniUtil jniUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        jniUtil = new JniUtil();

        btn_jni = (Button) findViewById(R.id.btn_jni);
        btn_jni.setOnClickListener(this);
        btn_jni_int = (Button) findViewById(R.id.btn_jni_int);
        btn_jni_int.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_jni:
                Toast.makeText(MainActivity.this, "返回的String为:" + jniUtil.getStringFromNative(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_jni_int:
                Toast.makeText(MainActivity.this, "和为:" + jniUtil.add(1, 3), Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

编写好代码后,执行Build–Make Project就会生成.class文件啦!
可以在app\build\intermediates\classes\debug\com\example\uidq0161\jnitest\MainActivity.class路径下找到生成的class文件。


然后打开Terminal终端
终端输入cd指令进入到app\src\main\java文件下,再输入javah指令。如下:

Terminal

javah -d ../jni -classpath  D:\android-sdk-windows\platforms\android-24\android.jar;D:\android-sdk-windows\extras\android\support\v4\android-support-v4.jar;D:\android-sdk-windows\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;D:\AndroidStudioProjects\JNITest\app\build\intermediates\classes\debug com.example.uidq0161.jnitest.JniUtil

javah的完整命令很长。。主要格式为 javah –d –jni –classpath。
不熟悉javah指令的可以输入javah -help。
可以看到
-d < dir > 输出目录

路径介绍:
D:\android-sdk-windows\platforms\android-24\android.jar 为此Android工程所依赖的sdk版本,最终锁定到android.jar文件;
D:\android-sdk-windows\extras\android\support\v4\android-support-v4.jar代表support-v4包
D:\android-sdk-windows\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar代表support-v7包
D:\AndroidStudioProjects\JNITest\app\build\intermediates\classes\debug com.example.uidq0161.jnitest.JniUtil代表生成头文件所依据的.class文件。两个路径中间要用英文分号隔开。

执行完上面的指令就会生成一个jni文件夹并在该文件夹下生成一个.h文件,如下:

这里写图片描述


新建c文件(c文件里面的方法应对应jni下的.h文件中的方法名),代码如下:

#include <jni.h>
#include <com_example_uidq0161_jnitest_JniUtil.h>

JNIEXPORT jstring JNICALL Java_com_example_uidq0161_jnitest_JniUtil_getStringFromNative
        (JNIEnv *env, jobject obj){
    return (*env)->NewStringUTF(env,"HELLO FROM NATIVEJNI");
};

JNIEXPORT jint JNICALL Java_com_example_uidq0161_jnitest_JniUtil_add
        (JNIEnv *env, jclass jobj, jint ja, jint jb) {
    return ja + jb;
}

编译运行,可以在app/build/intermediates/ndk/debug/lib目录下看到so文件

so

效果图如下:

效果图

后面补充一点C代码回调java方法
* ① 找到字节码对象
* //jclass (FindClass)(JNIEnv, const char*);
* //第二个参数 要回调的java方法所在的类的路径 “com/itheima/callbackjava/JNI”
* ② 通过字节码对象找到方法对象
* //jmethodID (GetMethodID)(JNIEnv, jclass, const char*, const char*);
* 第二个参数 字节码对象 第三个参数 要反射调用的java方法名 第四个参数 要反射调用的java方法签名
* javap -s 要获取方法签名的类的全类名 项目/bin/classes 运行javap
* ③ 通过字节码创建 java对象(可选) 如果本地方法和要回调的java方法在同一个类里可以直接用 jni传过来的java对象调用创建的Method
* jobject obj =(*env)->AllocObject(env,claz);
* 当回调的方法跟本地方法不在一个类里 需要通过刚创建的字节码对象手动创建一个java对象
* 再通过这个对象来回调java的方法
* 需要注意的是 如果创建的是一个activity对象 回调的方法还包含上下文 这个方法行不通!!!回报空指针异常
* ④ 反射调用java方法
* //void (CallVoidMethod)(JNIEnv, jobject, jmethodID, …);
* 第二个参数 调用java方法的对象 第三个参数 要调用的jmethodID对象 可选的参数 调用方法时接收的参数

例子源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值