Android Studio 在原有项目上NDK开发以及新建NDK开发项目

Android Studio 在原有项目上NDK开发以及新建NDK开发项目

这里的开发实例是目前项目中使用的一个串口操作,具体的c文件的编写根据你的项目实现;

  • 搭建编译环境
  • 编写调用c文件的方法并编译
  • 对class文件编译生成.h文件
  • 生成.c文件并关联.h文件
  • *生成Android.mk文件
  • 生成.so文件
  • CMakeList关联c文件与so
  • 新建NDK开发项目

搭建编译环境

打开Android Studio 选择 Tools -> Android ->SDK Manager

这里写图片描述
这里写图片描述
选择SDK Tools 然后勾选 CMake LLDB NDK 然后等他下载完成, 打开你的android studio的sdk目录 就会发现 有了一个叫做 ndk-bundle的文件夹,它就是ndk的地址

然后在配置这个地址
打开如下图
这里写图片描述

配置ndk路径

这里写图片描述
红色区域请根据您的目录相应更改

配置ndk的系统环境变量(也可以不配置,这是为了可以在cmd中使用ndk 命令)

这里写图片描述
这里写图片描述

win+r 打开cmd 输入ndk-build 如果出现以下显示即表示配置成功

这里写图片描述

为项目配置NDK

打开项目的local.propertiesgradle.properties
local.properties 中加入 ndk.dir=D:\WorkSoft\androidSdk\ndk-bundle(相应是你的目录)
这里写图片描述
gradle.properties 中加入android.useDeprecatedNdk=true
这里写图片描述

编写调用c文件的方法并编译

以下是android串口使用的java代码编译入口,可以根据你使用的c库改为你对应的java方法

编写java入口

public class SerialPort {
    private static final String TAG = "SerialPort";

    /*
     * Do not remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

        /* Check access permission */
        if (!device.canRead() || !device.canWrite()) {
            try {
                /* Missing read/write permission, trying to chmod the file */
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    throw new SecurityException();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
            }
        }

        mFd = open(device.getAbsolutePath(), baudrate, flags);
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

    // Getters and setters
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }

    // JNI
    //因为实在原项目(没勾选c++ suport情况下)这里的两个方法会爆红,不用管
    private native static FileDescriptor open(String path, int baudrate, int flags);
    public native void close();
    static {
    //注意这里是你的so库的名字
        System.loadLibrary("serial_port");
    }
}

编译class

ok 现在编译这个文件的class
这里写图片描述

然后找到这个class文件
这里写图片描述

对class文件编译生成.h文件

打开Terminal

打开android studio 自带的code工具 Terminal
这里写图片描述

定位在你的项目的 java文件夹 选择右击 copy path
这里写图片描述
这里写图片描述

在Terminal 输入 cd 刚才的路径path ctrl +v

这里写图片描述

编译生成.h文件

在Terminal 输入 javah -jni 类名
类名可以看你的 java 路径
这里写图片描述

这里写图片描述

这里java路径下就会生成一个包名+类名.h的文件,没有的话就关闭打开一下java目录

这里写图片描述

生成.c文件并关联.h文件

新建cpp文件夹

在main文件夹下新建cpp文件,并将刚才生成的h文件放入,然后在新建.c文件,文件名和h一样即可
这里写图片描述
这里写图片描述

编写c文件

要将h文件中的方法原封不动的复制过来,然后重写这个方法,(包括函数的参数,可以参考我的c代码)具体的c代码等
将h文件引入#include “com_pro_zhs_rxtest_SerialPort.h”

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "com_pro_zhs_rxtest_SerialPort.h"

static const char *TAG="serial_port";

static speed_t getBaudrate(jint baudrate)
{
    switch(baudrate) {
    case 0: return B0;
    case 50: return B50;
    case 75: return B75;
    case 110: return B110;
    case 134: return B134;
    case 150: return B150;
    case 200: return B200;
    case 300: return B300;
    case 600: return B600;
    case 1200: return B1200;
    case 1800: return B1800;
    case 2400: return B2400;
    case 4800: return B4800;
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    case 230400: return B230400;
    case 460800: return B460800;
    case 500000: return B500000;
    case 576000: return B576000;
    case 921600: return B921600;
    case 1000000: return B1000000;
    case 1152000: return B1152000;
    case 1500000: return B1500000;
    case 2000000: return B2000000;
    case 2500000: return B2500000;
    case 3000000: return B3000000;
    case 3500000: return B3500000;
    case 4000000: return B4000000;
    default: return -1;
    }
}

/*
 * Class:     com_youngch_common_myapplication_jniport_SerialPort
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
JNIEXPORT jobject JNICALL Java_com_pro_zhs_rxtest_SerialPort_open
   (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
{
    int fd;
    speed_t speed;
    jobject mFileDescriptor;

    /* Check arguments */
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            /* TODO: throw an exception */
//          LOGE("Invalid baudrate");
            return NULL;
        }
    }

    /* Opening device */
    {
        jboolean iscopy;
        const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
//      LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
        fd = open(path_utf, O_RDWR | flags);
//      LOGD("open() fd = %d", fd);
        (*env)->ReleaseStringUTFChars(env, path, path_utf);
        if (fd == -1)
        {
            /* Throw an exception */
//          LOGE("Cannot open port");
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Configure device */
    {
        struct termios cfg;
//      LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
//          LOGE("tcgetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }

        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);

        if (tcsetattr(fd, TCSANOW, &cfg))
        {
//          LOGE("tcsetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Create a corresponding file descriptor */
    {
        jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
        jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
        jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
        mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
        (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
    }

    return mFileDescriptor;
}
/*
 * Class:     com_youngch_common_myapplication_jniport_SerialPort
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_pro_zhs_rxtest_SerialPort_close
 (JNIEnv *env, jobject thiz)
 {
    jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
    jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

    jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

    jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
    jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

//      LOGD("close(fd = %d)", descriptor);
    close(descriptor);
   }

生成Android.mk文件

在main下新建文件夹jni

这里写图片描述
在项目module的build.gradle文件中配置

在android 下的defaultConfig 配置ndk

android{
    ...
     defaultConfig {
             ...
             ndk {
                    moduleName "serial_port"         //生成的so名字 与java代码中的引入的应该一样
                    abiFilters "armeabi", "armeabi-v7a", "x86"
                }
     }
}

然后就make project
这里写图片描述

将生成的Android.mk文件放入刚才新建的jni目录(如果没有生成nd文件目录,就将build删除重新make试试)

新建Application.mk

将文件放在jni目录中

这里写图片描述
Android.mk里内容如下

APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-14

你可以查看下你的android.mk文件的内容 看看你的路径都对不对

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := serial_port
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_SRC_FILES := \
    D:\ProWork\RxTest\app\src\main\jni\Android.mk \
    D:\ProWork\RxTest\app\src\main\jni\Application.mk \

LOCAL_C_INCLUDES += D:\ProWork\RxTest\app\src\main\jni
LOCAL_C_INCLUDES += D:\ProWork\RxTest\app\src\debug\jni

include $(BUILD_SHARED_LIBRARY)

在build.gradle中配置jni

android{
    ...
     defaultConfig {
             ...
     }
      //第一个是jni文件资源路径,第二个是生成so路径 此需要在buildTypes之前
    sourceSets {
        main
                {
                    jni.srcDirs = ['src/main/jni', 'src/main/']
                }
    }
    //在sourceSets之后
    buildTypes {
       ...
    }
}

这里写图片描述

ok

生成.so文件

复制jni文件夹路径

这里写图片描述

输入命令

打开Terminal
输入cd 刚才复制的路径 进入jni目录下并输入 ndk-build

这里写图片描述

如果这里ndk-build 不是内部或者外部命令,要么你的ndk系统变量配置错了要么就是你android studio的ndk路径配置错了,如果都没错,就可能是要重启一下android studio ,因为你修改系统变量后运行cmd等要重启才有用

CMakeList关联c文件与so

这个是没有支持c++的项目比较关键的一步骤

新建CMakeList.txt

这里写图片描述
这里写图片描述

编写CMakeLists.txt内容

cmake_minimum_required(VERSION 3.4.1)


add_library( # Specifies the name of the library.
# 这里是你so的名字。刚才在 MainActivity里面要引用的
             serial_port

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
              #这里是刚才 创建的c++ 代码的名字

             src/main/cpp/com_pro_zhs_rxtest_SerialPort.c )

find_library( # Sets the name of the path variable.
              log-lib

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

target_link_libraries( # Specifies the target library.
# 这里是你so的名字。刚才在 MainActivity里面要引用的 一样。
                       serial_port

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

配置cmake

在项目module的build.gradle文件中配置cmake

在Android 内编写

android{
    ...
     externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

ok等同步完成 你会发现java中的 爆红代码不爆红了
而且c文件中的代码也可以出现点击方法跳转,提示等

新建NDK开发项目

在创建项目的时候勾选 suport c++

就是项目会自动帮你生成cmake等,其他一样,比较简单,就不多bb了,有什么可以联系我哟~

路还很长,慢慢走

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值