Android ndk 开发入门(01)之 Hello World

前言

重新开始学习 Android ndk,为了以后方便复习和查找,我就把学习过程和资料整理成一个系列笔记吧。

Hello World

我们新建一个 Android Native C++ 项目,就叫 NdkDemo 吧,项目新建完成,我们编译运行一下看看效果。如图:
在这里插入图片描述
显示了一行字符串:Hello from C++。我们一行代码没写就已经实现了一个最简单的 ndk 程序,这行字符串是上层的 java 代码通过 jni 调用 native 层的代码显示出来的,也就是说最终实现是在 c++ 层,java 层只是调用,而 jni 就是这两者之间的桥梁。通过 jni, Java层的代码能够调用 c++ 的代码。那我们这篇文章就是来分析这个字符串是怎么显示出来的。

我们先看看 src/main 目录下的文件结构:
在这里插入图片描述
可以发现多了一个 cpp 文件夹,里面有两个新文件:CMakeLists.txt, native-lib.cpp。我们等会儿来看这两个文件,先看看 MainActivity.java 的代码:

public class MainActivity extends AppCompatActivity {

    // 在使用 native 方法之前,必须使用以下语句加载 jni 库,这里加载的 jni 库文件名为:libnative-lib.so
    // 我们写的时候可以省略掉前面的 lib, 系统会自动添加。
    static {
        System.loadLibrary("native-lib");
    }

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

        // 将 TextView 的文本设置为 native 方法 stringFromJNI() 返回的字符串。
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * native 方法声明,由 jni 层代码来实现。
     */
    public native String stringFromJNI();
}

看注释应该就能理解了,那我们会疑惑,这个 libnative-lib.so 是从哪儿来的呢?那就要看 cpp 目录下的 native-lib.cpp 文件,这个就是 jni 层的代码:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_along_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern “C” : 是让编译器按照 C 的编译规则进行编译。
JNIEXPORT 和 JNICALL : 这两个都是 jni 的关键字,表示此函数是要被JNI调用的。具体含义可以查一查。
jstring : jni 的一种数据类型,也就是方便 java 的 String 和 c++ 的 string 做转换,类似的还有 jint 等。后面文章会讲到数据类型的转换。
Java_com_along_ndkdemo_MainActivity_stringFromJNI : 方法名,也就是我们 java 层 native 方法的包名 + 类名 + 方法名的组合,其中"." 被替换成 “_”。java 层的 native 方法就是通过这种方法找到对应实现的。
JNIEnv : JavaVM 在线程中的代码,每一个线程都有一个, JNI 中可能有非常多个 JNIEnv。我们需要通过它在 jni 中操作 java 层的对象。
jobject : 对应 java 层中的对象。

接下来我们看看配置文件 — CMakeLists.txt,我们编写完 jni 的代码之后需要通过配置文件编译生成 .so 库文件。这里使用的是 CMake, 类似于 Android.mk 文件。

# CMake最低版本。
cmake_minimum_required(VERSION 3.4.1)

# 设置我们需要加载的库。
add_library(
             native-lib               # 库的名字。

             SHARED                   # 库的类型,这里指定为编译生成动态库。

             native-lib.cpp )         # 包含的原文件。


# 加载系统的一些库文件,更深层含义我暂定也不晓得。这里是加载 log 的库。
find_library(
              log-lib                 # Sets the name of the path variable.

              log )                   # Specifies the name of the NDK library that you want CMake to locate.


# 将生成的库链接到项目中。
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

解释都写在代码注释里了,之后我们编译一下会发现在 …/app/build/intermediates/cmake/debug/obj 目录下生成了四个平台对应的 .so 文件,如下图所示:
在这里插入图片描述
其实我们也可以指定生成指定平台的 .so 库,不用生成全部平台的,这样可以减少打包 app 的体积大小。方法如下:在 app/build.gradle 文件中指定:

android {
	...
    defaultConfig {
    	...
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_static'
            }
            ndk {
                abiFilters "armeabi-v7a", "x86"
            }
        }
        ...
    }
    ...
}

这样我们重新编译运行就可以看到只生成对应平台的 .so 库了。

在回到 MainActivity.java 中,我们就更能理解其中的代码了。首先在静态代码块中加载 libnative-lib.so 库,然后调用 native 方法:stringFromJNI(), 该方法的实现是在 jni 层,返回一个字符串:Hello from C++,然后在界面上显示该字符串,就是我们运行后看到的了。

参考资料:

1、[-NDK 导引篇 -] 在NDK开发之前你应知道的东西:
https://juejin.im/post/5d984028518825095879e45d
2、关于在Android中使用CMake你所需要了解的一切(该系列共有三篇文章):
https://juejin.im/user/58c382750ce4630054690774/posts

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值