使用NDK在Android中应用C/C++代码

概念

原生开发工具包(英语:native development kit,简称NDK)是一种基于原生程序接口的软件开发工具。通过此工具开发的程序直接以本地语言运行,而非虚拟机。因此只有java等基于虚拟机运行的语言的程序才会有原生开发工具包。
NDK帮助开发者快速地开发C(或C++)的动态库,自动将so和java应用一起打包到Apk
NDK提供工具以便JNI调用C/C++,而且提供了交叉编译器(交叉编译是在一个平台上生成另一个平台上的可执行代码。同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。)可以修改.mk文件生成特定CPU平台的动态库。

NDK和JNI的关系

简单说就是JNI负责Java与C/C++进行互相操作,NDK提供工具方便在Android平台使用JNI。

通过实例了解NDK的用法

环境要求

1.Android studio3.X(老版本操作不太一样)
2.AS里安装安卓虚拟机
3.jdk配置

正式开始

1.建立一个空的安卓工程

在这里插入图片描述

2.安装NDK

选择File->project Structure->SDK Location->Android NDK location
两种安装方式
1.官网下载,在这里填写路径
2.没安装的情况下这里会有一个蓝色的download点击自动安装最新版本
在这里插入图片描述
这里安装完local.properties里会自动加上路径
手动在gradle.properties里面加上:

android.useDeprecatedNdk=true

(新版本好像不用加,不过建议加一下,最多警告一下弃用)

3.新建jni类,依次点击app–>src–>main–>java–>com.example.myapplication新建一个java类

(创建出来是.class结尾的我这里是已经ndk-build后的)
在这里插入图片描述
代码如下:

package com.example.myapplication;
public class JNITest {
    JNITest(){
        System.loadLibrary("JniLib");
    }//加载动态链接库,名字要与后面建的一致
    public native String getString();
    //这个方法后面在C里面实现
}

4.在main下建一个jni文件夹来放.h头文件

在这里插入图片描述

5.建立头文件

alt+f12进入终端,直接是你的工程目录cd app/src/main
javac 命令生成.h头文件的格式为:

javac -encoding utf8 -h 目标文件夹  源文件夹

以我的为例

javac -encoding utf8 -h .\jni .\java\com\example\example\JNITest.java

然后就会发现生成了头文件
在这里插入图片描述
文件内容如下:(这个是自动生成的不用改)

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

#ifndef _Included_com_example_myapplication_JNITest
#define _Included_com_example_myapplication_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_JNITest
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_myapplication_JNITest_getString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

6.在jni目录下新建:Android.mk、Application.mk

Android.mk

LOCAL_PATH := $(call my-dir) #不用修改
include $(CLEAR_VARS) #不用修改
LOCAL_MODULE := JniLib #动态库名称和上面的一样
LOCAL_SRC_FILES =: JniLib.cpp #Cpp文件,里面就是我们写的Cpp代码
include $(BUILD_SHARED_LIBRARY) #生成.so动态库

Application.mk

APP_MODULES := JniLib
APP_ABI := all

7.编写JniLib.cpp

在jni文件夹里建c++文件JniLib.cpp(和上面名字一样)
这里基本是头文件里的加个参数就能用了包名按自己的改用"下划线_"连接

#include <jni.h>
#include<com_example_myapplication_JNITest.h>
#include<stdio.h>
#include<iostream>
extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_JNITest_getString
             (JNIEnv * env, jobject clazz){
             return env->NewStringUTF("C++ Hello World");
             }

8、将java文件链接到C++文件:

右键java文件夹–》Link C++…->选择ndk-build,Project Path是Android.mk的地址

9.添加ndk节点、sourceSets节点、task ndkBuild节点:

进入app文件夹下的build.gradle文件:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        ndk{
            moduleName "JniLib"
            abiFilters  "armeabi-v7a", "x86" //输出指定的三种abi体系下的so库"armeabi",只加载armabi架构(目录下)的so库,如果是别的架构,就会找不到
        }
        sourceSets{  //不配的话都会有一个默认值  可以指定哪些源文件(或文件夹下的源文件)要被编译,哪些源文件要被排除
            main{
                jni.srcDirs = []  //禁用as自动生成mk
                //jniLibs.srcDirs=["src/main/libs" ] //so包就去src/main/libs目录下找
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    task ndkBuild(type:Exec,description:'Compile JNI source via NDK'){
        commandLine "D:\\androidsdk\\ndk-bundle\\ndk-build.cmd",//配置ndk的路径
                'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk默认的生成so的文件
                'NDK_LIBS_OUT=src/main/libs',//配置的我们想要生成的so文件所在的位置
                'APP_BUILD_SCRIPT=src/main/jni/Android.mk',//指定项目以这个mk的方式
                'NDK_APPLOCATION_MK=src/main/jni/Application.mk'//指定项目以这个mk的方式
    }
    externalNativeBuild {
        ndkBuild {
            path file('src/main/jni/Android.mk')
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

10.生成动态库

在工具里配置ndk-build
program是配ndk-build.cmd的地址在上面安装的ndk-build里面
Working directory是你的工程目录
在这里插入图片描述
选中JNITest类右键->External Tools->ndk-build,生成so文件:
在这里插入图片描述

11.mainActivity和布局文件

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    Button button;
    TextView ndk;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        ndk = findViewById(R.id.ndk);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ndk.setText("结果:"+ new JNITest().getString());
                //tv.setText("结果:");
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/ndk"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="---调用之前---"
    android:textSize="@android:dimen/app_icon_size"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
        android:text="调用JNI"
    tools:layout_editor_absoluteX="30dp"
    tools:layout_editor_absoluteY="30dp"
    tools:ignore="MissingConstraints"/>

</androidx.constraintlayout.widget.ConstraintLayout>

12.运行结果

点击调用之前
在这里插入图片描述
点击调用
在这里插入图片描述

作者:韦天钰
原文链接:https://blog.csdn.net/qq_43425012/article/details/106728040


版权声明:本文为fjnu_se原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。