AndroidNDK开发——使用Cmake编译生成so文件

文章目录

  • AndroidNDK开发——使用Cmake编译生成so文件
  • 1.添加Cmake文件:
  • 2.添加Cmake依赖:
  • 3.jni文件如下:
  • 4.Android.mk文件:
  • 5.Application.mk文件
  • 6.SerialPort.c文件:
  • 7.SerialPort.h文件:
  • 8.运行项目:
  • 9.项目中引入so文件:
  • 10.Java调用so的测试代码如下:
  • 11.运行效果如下:
  • 12.打印日志如下:
  • 13.总结:
  • 14.项目源码如下:

AndroidNDK开发——使用Cmake编译生成so文件

最近做串口开发需要编译不同的so文件,于是查找了各种资料,学习了一下so编译.

1.添加Cmake文件:

cmake_minimum_required(VERSION 3.4.1) //cmake版本

include_directories(src/main/jni/serial_port)   //引入的jni文件路径
aux_source_directory(src/main/jni SRC_FILE)		//src文件路径

add_library(serial_port SHARED ${SRC_FILE})
find_library(log-lib log)

target_link_libraries(serial_port ${log-lib})

AndroidNDK开发——使用Cmake编译生成so文件_第1张图片

2.添加Cmake依赖:

externalNativeBuild {
    cmake {
        cppFlags ""

        abiFilters "arm64-v8a","armeabi-v7a"//生成的so平台类型

    }
}

AndroidNDK开发——使用Cmake编译生成so文件_第2张图片

externalNativeBuild {
    cmake {
        path 'CMakeLists.txt'//Cmake地址
    }
}

AndroidNDK开发——使用Cmake编译生成so文件_第3张图片

3.jni文件如下:

AndroidNDK开发——使用Cmake编译生成so文件_第4张图片

4.Android.mk文件:

#
# Copyright 2009 Cedric Priscal
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. 
#

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

TARGET_PLATFORM := android-3
LOCAL_MODULE    := serial_port  //模块名称
LOCAL_SRC_FILES := SerialPort.c
LOCAL_LDLIBS    := -llog

include $(BUILD_SHARED_LIBRARY)

5.Application.mk文件

APP_ABI := arm64-v8a,armeabi-v7a//配置abi平台

6.SerialPort.c文件:

/*
 * Copyright 2009-2011 Cedric Priscal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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

#include "SerialPort.h"

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

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:     android_serialport_SerialPort
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_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, "", "()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:     cedric_serial_SerialPort
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_android_1serialport_1api_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);
}

jstring Java_com_example_compilesodemo_MainActivity_sayHello(JNIEnv* env,jobject jobj){
   char* text="I am from C";
   return (*env)->NewStringUTF(env,text);
}

7.SerialPort.h文件:

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

#ifndef _Included_android_serialport_api_SerialPort
#define _Included_android_serialport_api_SerialPort
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     android_serialport_api_SerialPort
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
  (JNIEnv *, jclass, jstring, jint, jint);

/*
 * Class:     android_serialport_api_SerialPort
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
  (JNIEnv *, jobject);


#ifdef __cplusplus
}
#endif
#endif

8.运行项目:

直接跑项目或者使用gradle构建so生成的so文件如下:这里大家根据自己的需求配置生成so文件,本文只是举例没有全部生成.
AndroidNDK开发——使用Cmake编译生成so文件_第5张图片

9.项目中引入so文件:

如果不引入so文件初始化和调用时会报错,提示找不到so文件,一定要记得配置!一定要记得配置!一定要记得配置!
AndroidNDK开发——使用Cmake编译生成so文件_第6张图片
AndroidNDK开发——使用Cmake编译生成so文件_第7张图片

10.Java调用so的测试代码如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getName();

    {
        System.loadLibrary("serial_port");//加载so库
    }

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

    private void initView() {
        TextView textView =  ((TextView)findViewById(R.id.tv_test));
        textView.setText(sayHello());
        Log.d(TAG,"-----传入的数据为:------"+ textView.getText().toString());
    }

    public native String sayHello();//调用jni方法
}

11.运行效果如下:

AndroidNDK开发——使用Cmake编译生成so文件_第8张图片

12.打印日志如下:

AndroidNDK开发——使用Cmake编译生成so文件_第9张图片

13.总结:

可以看到项目demo成功运行,期前也遇到不少问题,so的加载和调用,jni初始化,数据传递等等,如果有兴趣的同学可以自己去试试,学到手的东西才是自己的。本文是以串口通信的so为例,所以里面的头文件及数据调用的方法都是在加载串口数据,不过我为了测试所以暂时只写了一个简单的,因为测试串口需要设备和串口通信助手,这里先不讲,后面有空会逐一讲解串口通信流程和实战demo.

14.项目源码如下:

https://gitee.com/jackning_admin/compilesodemo

你可能感兴趣的:(Android串口开发,android,kotlin,android,studio)