在Android Studio下进行NDK开发

什么是JNI?

JNI的全称就是Java Native Interface,其实它就是一种协议。一说协议,那它就是对某种东西的一个规范和约束,说的好听一点就是标准化。意思是如果你想用我这个东西,那你必须要遵守我这边的规范。有了JNI这个协议,Java和C/C++代码才能相互调用。

什么是NDK

NDK全称是Native Development Kit,它就是一个本地开发的“工具包”。Java开发要使用JDK,Android开发要使用SDK,那我们在Android中要进行native开发,也要用到它对应的工具包,即NDK。通俗的来讲,NDK就是帮助我们可以在Android应用中使用C/C++来完成特定功能的一套工具。 NDK的作用有很多,我们简单的列举两个,比如:

1.首先,NDK可以帮助开发者“快速”开发C(或C++)的动态库。
2.其次,NDK集成了“交叉编译器”。使用NDK,我们可以将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。

       上面提到了“交叉编译”,我们最后再解释一下什么是交叉编译。大家都知道编译器在将中间代码连接成当前计算机可执行的二进制程序时,连接程序会根据当前计算机的CPU、操作系统的类型来转换。而根据运行的设备的不同,CPU的架构也是不同,大体有如下三种常见的CPU架构:

arm结构 :主要在移动手持、嵌入式设备上。我们的手机几乎都是使用的这种CPU架构。
x86结构 : 主要在台式机、笔记本上使用。如Intel和AMD的CPU 。
MIPS架构:多用在网关、猫、机顶盒等设备。

       若想在使用了基于x86架构CPU的操作系统上编译出可以在基于arm结构CPU的操作系统上运行的代码,就必须使用交叉编译。所以综上所述:交叉编译就是在一个平台下(比如:CPU架构为X86,操作系统为Windows)编译出在另一个平台上(比如:CPU架构为arm,操作系统为Linux)可以执行的二进制代码。Google提供的NDK就可以完成交叉编译的工作。

好了,上面的基本概念介绍完以后,我们正式进入AS下NDK开发的讲解。
1.打开Android Studio,点击SDK Manager,切换到SDK Tools,选择NDK和CMake并下载;
2.新建一个工程Test,打开local.properties,添加NDK目录:ndk.dir=/Users/Enzo/Library/Android/sdk/ndk/25.2.9519653。
3.新建activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>
</LinearLayout>

4.新建MainActivity.java:

public class MainActivity extends AppCompatActivity {

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

        final TextView textview = findViewById(R.id.textview);
        Button button = findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textview.setText(JNIUtils.sayHelloFromJNI());
            }
        });
    }
}    

新建JNIUtils.java:

package com.example.test;

public class JNIUtils {

    public static native String sayHelloFromJNI();

}

5.接下来我们要生成sayHelloFromJNI()方法所对应的头文件,打开AS里面的Terminal,cd到JNIUtils.java对应的目录下,执行如下命令:

javac -h ./ JNIUtils.java

执行完后,对应的头文件com_example_test_JNIUtils.h已经生成了。可以执行javac -help看看javac -h的含义:

用法: javac <options> <source files>
其中,可能的options包括:
  -h <directory>               指定放置生成的本机标头文件的位置

头文件com_example_test_JNIUtils.h的代码如下:

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

#ifndef _Included_com_example_test_JNIUtils
#define _Included_com_example_test_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_test_JNIUtils
 * Method:    sayHelloFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_test_JNIUtils_sayHelloFromJNI
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

6.我们在src/main/目录下新建jni目录,把头文件放进去,接下来在jni目录下新建C++文件,命名为JNIHello.cpp,代码如下:

#include "com_example_test_JNIUtils.h"
JNIEXPORT jstring JNICALL Java_com_example_test_JNIUtils_sayHelloFromJNI
        (JNIEnv *env, jclass jclass){
return env->NewStringUTF("Hello World From JNI!!!!!");
}

这里可以看到我们首先需要把原来生成的JNIUtlis对应的头文件引入进来,下面的代码基本都是从com_example_test_JNIUtils.h中复制粘贴过来的一部分,然后稍加修改。修改的地方主要有sayHelloFromJNI的两个参数和里面的简单实现,参数方面就是加了env和jclass两个字段。函数里面的实现呢,就是简单的返回一个字符串“Hello World From JNI!!!”,大家现在就需要知道如果要在这里返回一个字符串就必须要通过env->NewStringUTF(“xxxxxx”);这行代码。

7.最后我们还要在JNIUitls.java中加载生成的动态库:

public class JNIUtils {
    static {
        System.loadLibrary("JNIHello");
    }
    public static native String sayHelloFromJNI();
}

此时执行项目,项目报错:

com.example.test E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.test, PID: 5594
    java.lang.UnsatisfiedLinkError: dlopen failed: library "libJNIHello.so" not found

找不到so库报错。

8.继续在jni目录中添加Android.mk和Application.mk,Android.mk代码如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNIHello
LOCAL_SRC_FILES := JNIHello.cpp
include $(BUILD_SHARED_LIBRARY)

Application.mk代码如下:

APP_ABI := all

9.cd到jni目录下,执行ndk-build命令(需要提前配置NDK目录的环境变量),执行成功后会在src/main/libs下生成对应的so文件。

10.在app的build.gradle中添加:

android {
 	sourceSets {
        main(){
            jniLibs.srcDirs = ["src/main/libs"]
            jni.srcDirs = []
        }
    }
}   

重新运行程序,点击Button,成功显示:Hello World From JNI!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值