NDK学习笔记(1)——第一个jni程序

环境配置

以Android studio 2.2为例,点击tools->Android->SDKManager。

勾选并下载 CMake、LLDB、NDK: CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性; LLDB:调试本地代码工具; NDK:Android 和 C/C++交互的工具。 点击ok后会进入下载安装界面,速度视网速而定。 下载完成后,在SDK目录下会多出一个NDK文件夹:

然后需要配置下系统的环境变量: 在用户变量里添加刚刚存放ndk-bundle的路径。

新建项目

  1. 新建一个项目:

注意点选include c++ support,因为AS对c语言的支持不够好,如果不选直接创建jni项目虽然可以运行但是某些地方会被标注为红色且无法使用提示功能。 其他一路next就好。

  1. 因为我们选择了支持c++,所以local.properties里自动添加了相关代码
ndk.dir=D\:\\toolSoftwore\\androidSDK\\ndk-bundle
sdk.dir=D\:\\toolSoftwore\\androidSDK

如果只是普通的项目,需要添加ndk.dir=D:\toolSoftwore\androidSDK\ndk-bundle。 3. 在gradle.properties里面声明使用NDK的代码

android.useDeprecatedNdk=true

这段代码的作用在于兼容以前版本的NDK。

CMakeLists

在AS 2.2 以后的版本里,多了一个配置文件

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.
# 设定CMake编译本地库的最低版本,你可以保持为默认值,也可以修改为更低

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
# 创建并且命名库,并将库文件设置为STATIC或者SHARED,并且提供到达库所在的源码的相关路径
# 你可以定义多个库,CMake将编译他们,Gradle将自动打包被标识为SHARED的库到你的APK中。

add_library( # 这个是声明引用so库的名称,在项目中,如果需要使用这个so文件,引用的名称就是这个。
             # 值得注意的是,实际上生成的so文件名称是libnative-lib。当Run项目或者build项目是
             # 在Module级别的build文件下的
             native-lib

             # 设置该库为SHARED类型
             SHARED

             # 提供一个到达该库的相对路径
             # 这个目录下的文件会被自动包括在内
             src/main/cpp/native-lib.cpp )

# 这个方法与我们要创建的so库无关而是使用NDK的Apis或者库,默认情况下Android平台集成了很多NDK库文件
# 所以这些文件是没有必要打包到apk里面去的。直接声明想要使用的库名称即可。
# 在这里不需要指定库的路径,因为这个路径已经是CMake路径搜索的一部分。

find_library( # 设定路径变量的名字
              log-lib

              # 声明你需要CMake去定位的NDK库的名字
              log )

# 如果你本地的库(native-lib)想要调用log库的方法,那么就需要配置这个属性,
# 意思是把NDK库关联到本地库。

target_link_libraries( # 要被关联的库名称
                       native-lib

                       # 要关联的库名称,要用大括号包裹,前面还要有$符号去引用。
                       ${log-lib} )

库类型分为以下三种:

STATIC:静态库,是目标文件的归档文件,在链接其它目标的时候使用。
SHARED:动态库,会被动态链接,在运行时被加载。
MODULE:模块库,是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接。

build.gradle

该文件也多出了一些代码。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.ndkdemo.ustc.jnitest"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                //如果你在创建工程选择C++11的标准,则使用cppFlags “-std=c++11”
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            //当Run或者Build项目时,想要执行CMakeLists.txt构建脚本,需要把脚本配置到模块级的build.gradle中。
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    testCompile 'junit:junit:4.12'
}

原有的代码

java文件:

public class MainActivity extends AppCompatActivity {

    // 用来在系统启动时加载本地库,必须要加
    static {
        System.loadLibrary("native-lib");
    }

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

        // 调用本地方法的范例
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * 如果一个方法被打包在应用里的本地库所实现的话,那么,它就是本地方法
     */
    public native String stringFromJNI();
}

C代码

//jni.h头文件包括了一系列java与c语言交互的方法
#include <jni.h>
#include <string>

//extern关键字标明下面方法使用c语言的编译器进行编译
extern "C"
/*
 * 返回值 jstring是jni.h中定义的对应java中string的类型
 * 函数名由java的包名和方法名拼接而成
 * @para JNIEnv 是一个线程相关的结构体指针,可以用来调用本地函数
 * @para jobject 当前对象的指针
 */
jstring Java_com_ndkdemo_ustc_jnitest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */)
{
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

添加本地方法

先在MainActivity里添加一个本地方法:

public native String helloFromC();

然后再使用alt+enter自动添加一个c++函数:

extern  "C"
JNIEXPORT jstring JNICALL
Java_com_ndkdemo_ustc_jnitest_MainActivity_helloFromC(JNIEnv *env, jobject instance) {

    // TODO
    std::string hello = "Hello from C";
    return env->NewStringUTF(hello.c_str());
}

其中,JNIEXPORT没什么用,JNICALL可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX。这两个关键字都可以省略。 然后修改一下调用的方法,运行。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏老九学堂

【新手必读】Java初学者,你遇到的问题都在这了

我们在初次接触某种编程语言时,都会有许许多多的疑问和困惑,老九君收集了小伙伴遇到的一些常见的Java基础问题,希望能对大家的Java学习有所帮助。 初识篇 1、...

3035
来自专栏代码GG之家

android dumpsys 快速入门

dumpsys属于android平台上的一个bin文件,放置在/system/bin 下面,主要完成打印系统服务的信息,帮助调试分析解决问题。 之前写的文章太...

1786
来自专栏JavaQ

记一次java.lang.NoSuchMethodError

当思路如泉涌般、很流程的写完一段代码,点击Run看看执行结果的时候,Duang的一下输出了一串“Caused by: java.lang.NoSuchMetho...

40113
来自专栏AzMark

Python 学习之面向对象「下」

1083
来自专栏james大数据架构

Eclipse与Android源码中ProGuard工具的使用

由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手...

2099
来自专栏蜉蝣禅修之道

EJBCA使用之注册用户及创建证书

2124
来自专栏Golang语言社区

go语言实现http服务端与客户端

go语言的net/http包的使用非常的简单优雅 (1)服务端 [plain] view plain copy package main import ...

3347
来自专栏一“技”之长

Java开发GUI之列表 原

    awt包中的List控件可以创建一个选择列表,此列表可以支持单选,也可以支持多选。

1092
来自专栏xdecode

Java过滤XSS脚本, 可通过Appscan扫描

项目中有时会需要把一些报错或者解决方案直接返回给前端, 如果直接返回原字符串, 可能会被恶意传参来实现xss注入. 例如常规业务访问一个页面读取文件&file=...

2615
来自专栏深度学习之tensorflow实战篇

Python 用OPEN读文件报错 ,路径以及r

Python 中 ‘unicodeescape’ codec can’t decode bytes in position XXX: trun错误解决方案 背...

3576

扫码关注云+社区