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就好。
  2. 因为我们选择了支持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。这两个关键字都可以省略。
然后修改一下调用的方法,运行。
这里写图片描述

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kevinjqy/article/details/72804817
文章标签: android jni ndk
个人分类: JNI学习笔记
想对作者说点什么? 我来说一句

JNI 编程资料

2014年04月28日 341KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭