JNI入门-第一个实例

NDK简介

官方文档介绍如下:

The Android NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C++. For certain types of apps, this can help you reuse code libraries written in those languages.

大概意思就是NDK它是一个工具集, 它可以让你使用C/C++ 语言来实现APP的某些部分。对于某些类型的应用,NDK可以帮助你使用C/C++重写代码库。

与NDK密切相关的另一个词汇则是JNI,它是NDK开发中的枢纽,Java与C/C++层交互基本上就是是通过它来完成的,那么什么是JNI?

JNI:Java Native Interface 也就是java本地接口,它是一个协议,这个协议用来沟通java代码和本地代码(c/c++)。通过这个协议,Java类的某些方法可以使用原生实现,同时让它们可以像普通的Java方法一样被调用和使用,而原生方法也可以使用Java对象,调用和使用Java方法。也就是说,使用JNI这种协议可以实现:java代码调用c/c++ 代码,而c/c++代码也可以调用java代码。

环境准备

  • Android Studio
  • Android SDK
  • Android NDK

使用AS创建好一个Project后,需要注意几个地方的配置:

    1.在项目gradle.properties文件中加上以下代码,表示我们要使用NDK进行开发。

android.useDeprecatedNdk=true

    2.在项目local.properties中加入ndk和sdk的路径

// 这是我的ndk及sdk安装路径
ndk.dir=D\:\\software\\JAVA\\android-sdk-windows\\ndk-bundle
sdk.dir=D\:\\software\\JAVA\\android-sdk-windows

    3.在app文件夹下的build.gradle中的defaultConfig里加入如下代码

android {
    ......

    defaultConfig {
        ......

        ndk {
            moduleName "MyFirstJinTest"//指定生成的so文件名
            abiFilters "armeabi-v7a", "x86"//cpu的类型
        }
    }

    ......
}

第一步:创建好一个类

创建一个Java类,例如我的是MyFirstJniTest.java,具体代码如下:

package com.example.androidqunyinhui.jni;
public class MyFirstJniTest {

    static {
        //此名字必须和build.gradle中的ndk下moduleName一致
        try {
            System.loadLibrary("MyFirstJinTest");
            Log.i("JNI", "MyFirstJinTest load success");
        } catch (Exception e) {
            Log.e("JNI", "MyFirstJinTest load error");
            e.printStackTrace();
        }
    }

    public static native String getString();
    public static native int add(int a, int b);

}

第二步:创建对应的.h头文件

在完成第一步之后,我们在AS的命令行中输入如下命令:

// 进入到java目录
cd app/src/main/java
// javah是进行头文件生成
// 例如 我的为 avah -jni com.example.androidqunyinhui.jni.MyFirstJniTest
javah -jni 包名.类名

此时在java目录下会看到com_example_androidqunyinhui_jni_MyFirstJniTest.h文件

内容大致如下:

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

#ifndef _Included_com_example_androidqunyinhui_jni_MyFirstJniTest
#define _Included_com_example_androidqunyinhui_jni_MyFirstJniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_androidqunyinhui_jni_MyFirstJniTest
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_getString
  (JNIEnv *, jclass);

/*
 * Class:     com_example_androidqunyinhui_jni_MyFirstJniTest
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_add
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

粗略的看,我们会看到两个和我们在MyFirstJniTest类中定义函数名相同的方法:getString和add,只不过前面加了一堆前缀。

第三步:新建jni文件夹,并拷贝上面生成的.h文件到jni目录

选中src,右键:New->Folder->JNI Folder

点击finish,此时我们可以看到在src目录下创建了一个jni目录。

将上面生成的.h文件拷贝到该jni文件加下。

第四步:在jni目录下,右键新建C文件,文件名任意

在jni目录下,右键新建C文件,文件名任意。

该.C文件的作用就是对.h文件的方法进行实现,例如创建的文件名为MyFirstJniTest.c,具体内容如下:

#include <com_example_androidqunyinhui_jni_MyFirstJniTest.h>

//返回一个字符串
JNIEXPORT jstring JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_getString
  (JNIEnv *env, jclass jobj){
    return (*env)->NewStringUTF(env,"MyFirstJniTest 我是用jni调用出来的字符串");
}
//返回 a+b的结果
JNIEXPORT jint JNICALL Java_com_example_androidqunyinhui_jni_MyFirstJniTest_add
  (JNIEnv *env, jclass jobj, jint a, jint b){
  return a+b;
}

第五步:选择 Build->Make Project

选择 Build->Make Project,这时候可以看到在app/build/intermediates/ndk/debug/lib目录下生成了对于的.so文件。

如果没有生成,选择 Build->Clean Project,等clean完成后,再Build->Rebuild Project,一般经过上面两步以后都能够解决问题。

完成以上步骤,基本上算是完成了一个小型的JNI程序,我们编写C/C++代码,Java层如何去调用。

但是。。但是。。。

在真实的项目开发中,我们到这里并没有结束,通常情况下,我们不会将C相关代码也写到Android 工程文件下,Android中需要调用C/C++相关代码,一般都是通过so文件加载的,so文件通常是放在app/jniLibs或者是app/libs目录下。这样的好处相信大家都明白,不在说明。

需要注意的是,当我们把so文件放在app/libs目录下的时候,还需要在app下的build.gradle文件做如下配置

android {
    ......
    sourceSets {
        main {
            jniLibs.srcDir 'libs'
        }
    }
    ......
}

这是因为在加载so文件的时候,默认是app/jniLibs文件夹,因此当改变文件夹的时候,需要对jniLibs.srcDir 指定路径。

下面看看我的demo最终示例效果

  • activity中代码如下
public class JniTestActivity extends AppCompatActivity {

    public static void startActivity(Context context){
        Intent intent = new Intent(context, JniTestActivity.class);
        context.startActivity(intent);
    }


    private TextView tvShowInfo;

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

        initView();
    }

    private void initView(){

        tvShowInfo = (TextView) findViewById(R.id.tv_show_info);

        findViewById(R.id.btn_show_info).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String text = MyFirstJniTest.getString();
                int sum = MyFirstJniTest.add(1, 2);
                tvShowInfo.setText(text+"   sum="+sum);
            }
        });
    }
}

参考文献

https://www.cnblogs.com/guanmanman/p/6769240.html

https://blog.csdn.net/u011652925/article/details/78272406

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值