您现在的位置是:网站首页> Android

Android中Java与C++交互及库开发与使用

  • Android
  • 2025-06-15
  • 846人已阅读
摘要

Android中Java与C++交互及库开发与使用


手机连接等硬件问题汇集

简单理理AndroidStudio 生成SO并调用

AndroidStudio引用第三方so库的正确姿势

Android引用jar和so

Java数据类型对照C++类型

库开发使用

Android Studio引用本地jar包的方法

如何将android apk编译为arr包

Android引用arr包的两种方法

Android studio 导出,导入 arr包

常见问题

Android打aar没有包含so的解决办法



简单理理AndroidStudio 生成SO并调用

工程结构如下

1.png

文件结构如下:

1.png


在调用的地方定义在 MainActivity类里



public class MainActivity extends AppCompatActivity {


    // Used to load the 'native-lib' library on application startup.

    static {

        System.loadLibrary("native-lib");

    }

public native String stringFromJNI();

...

}


在native-lib.cpp中实现


#include <jni.h>

#include <string>


extern "C" JNIEXPORT jstring JNICALL

Java_com_xn_helloso_MainActivity_stringFromJNI(

        JNIEnv* env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}


函数名称就是Java+包地址+类+函数名

编译需要在module下的build.gradle文件里加

externalNativeBuild {

        cmake {

            path "CMakeLists.txt"

        }

    }

生成的so动态库在:app\build\intermediates\cmake\debug\obj

CMakeLists.txt文件内容


# For more information about using CMake with Android Studio, read the

# documentation: https://d.android.com/studio/projects/add-native-code.html


# Sets the minimum version of CMake required to build the native library.


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 them for you.

# Gradle automatically packages shared libraries with your APK.


add_library( # Sets the name of the library.

             native-lib


             # Sets the library as a shared library.

             SHARED


             # Provides a relative path to your source file(s).

             src/main/cpp/native-lib.cpp )


# Searches for a specified prebuilt library and stores the path as a

# variable. Because CMake includes system libraries in the search path by

# default, you only need to specify the name of the public NDK library

# you want to add. CMake verifies that the library exists before

# completing its build.


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 )


# Specifies libraries CMake should link to your target library. You

# can link multiple libraries, such as libraries you define in this

# build script, prebuilt third-party libraries, or system libraries.


target_link_libraries( # Specifies the target library.

                       native-lib


                       # Links the target library to the log library

                       # included in the NDK.

                       ${log-lib} )



AndroidStudio引用第三方so库的正确姿势

1、把so文件复制到 \app1\app\libs\ 文件夹下,但是要注意,so文件是放在对应的平台文件夹之下(如arm64-v8a,armeabi-v7a, x86,x86_64),这点非常重要,否则不能成功引用,每个平台文件夹下都放上该so文件,如下图:

1.png


2.png


 


2、AndroidStudio打开项目,并切换到 Android 栏,并打开Gradle Scripts\build.gradle(Module:app1.app) ,加入 节点


sourceSets{

    main{

        jniLibs.srcDirs "libs"

    }

}

如下图:

3.png 


 


3、加完之后,有一个刷新(同步)的操作 ,之后在app下就可以看到jniLibs文件夹,如下:

4.png


通过JNI定义实现调用第三方库so如上面的native-lib.cpp文件



Android引用jar和so

其实jni、jniLibs一个是C++源码提供一个是C++编译成so提供,libs、java一个是以Java编译的jar,一个是以Java代码提供

1.png


ModuleAPI.c

#include <jni.h>

#include <ModuleAPI.h>

#include <Logger.h>

#include <SerialPort.h>

#include <jni.h>

static const char *TAG = "ModuleAPI";

//#define MSG_CRC_INIT     0xFFFF

//#define MSG_CCITT_CRC_POLY 0x1021

//void CRC_calcCrc8(uint16_t *crcReg, uint16_t poly, uint16_t u8Data)

//{

//    uint16_t i;

//    uint16_t xorFlag;

//    uint16_t bit;

//    uint16_t dcdBitMask = 0x80;

//    for(i=0; i<8; i++)

//    {

//        xorFlag = *crcReg & 0x8000;

//        *crcReg <<= 1;

//        bit = ((u8Data & dcdBitMask) == dcdBitMask);

//        *crcReg |= bit;

//        if(xorFlag)

//        {

//            *crcReg = *crcReg ^ poly;

//        }

//        dcdBitMask >>= 1;

//    }

//}

//

//uint16_t CalcCRC(uint8_t *msgbuf,uint8_t msglen)

//{

//    uint16_t calcCrc = MSG_CRC_INIT;

//    uint8_t  i;

//    for (i = 1; i < msglen; ++i)

//        CRC_calcCrc8(&calcCrc, MSG_CCITT_CRC_POLY, msgbuf[i]);

//    return calcCrc;

//}




#define MSG_CRC_INIT     0xFFFF

#define MSG_CCITT_CRC_POLY 0x1021

#define uint16 unsigned short

#define uint8 unsigned char


void CRC_calcCrc8(uint16 *crcReg, uint16 poly, uint16 u8Data) {

    uint16 i;

    uint16 xorFlag;

    uint16 bit;

    uint16 dcdBitMask = 0x80;

    for (i = 0; i < 8; i++) {

        xorFlag = *crcReg & 0x8000;

        *crcReg <<= 1;

        bit = ((u8Data & dcdBitMask) == dcdBitMask);

        *crcReg |= bit;

        if (xorFlag) {

            *crcReg = *crcReg ^ poly;

        }

        dcdBitMask >>= 1;

    }

}


uint16 CalcCRC(uint8 *msgbuf, uint8 msglen) {

    uint16 calcCrc = MSG_CRC_INIT;

    uint8 i;

    for (i = 0; i < msglen; ++i)

        CRC_calcCrc8(&calcCrc, MSG_CCITT_CRC_POLY, msgbuf[i]);

    return calcCrc;

}



JNIEXPORT jint JNICALL Java_com_xlzn_hcpda_ModuleAPI_SerailOpen(JNIEnv* env, jobject thiz, jstring juart,jint baudrate, jint databits,jint stopbits, jint check) {

    jboolean iscopy;

     LOGD(TAG, "Java_com_xlzn_hcpda_ModuleAPI_SerailOpen");

     const char *path_uart = (*env)->GetStringUTFChars(env, juart, &iscopy);

    int result= SerialPort_Open(path_uart,   baudrate,   databits,   stopbits,  check);

    (*env)->ReleaseStringUTFChars(env, juart, path_uart);

    return result;

    return 0;

}



JNIEXPORT jint JNICALL Java_com_xlzn_hcpda_ModuleAPI_SerailClose(JNIEnv *env, jobject thiz,int uart_fd) {

    int result= SerialPort_Close(uart_fd);

    return result;

}


JNIEXPORT jint JNICALL Java_com_xlzn_hcpda_ModuleAPI_SerailSendData(JNIEnv *env, jobject thiz, int uart_fd,jbyteArray send_data,int sendLen) {

    unsigned char uData[sendLen];

    jbyte *jpszData = (*env)->GetByteArrayElements(env, send_data, 0);

    for (int i = 0; i < sendLen; i++) {

        uData[i] = jpszData[i];

    }

    int reuslt= SerialPort_Send(uData,sendLen,uart_fd);

    (*env)->ReleaseByteArrayElements(env, send_data , jpszData, 0);

    return reuslt;

}


JNIEXPORT jint JNICALL Java_com_xlzn_hcpda_ModuleAPI_SerailReceive(JNIEnv *env, jobject thiz, jint uart_fd,jbyteArray receive_data,int receive_dataLen) {

    unsigned char uData[receive_dataLen];

    int reuslt= SerialPort_Receive(uData,receive_dataLen,uart_fd);

    if(reuslt>0){

        jbyte *jpszData = (*env)->GetByteArrayElements(env, receive_data, 0);

        for (int i = 0; i < reuslt; i++) {

            jpszData[i] = uData[i];

        }

        (*env)->ReleaseByteArrayElements(env, receive_data , jpszData, 0);

    }

    return reuslt;

}

JNIEXPORT jint JNICALL Java_com_xlzn_hcpda_ModuleAPI_CalcCRC(JNIEnv *env, jobject thiz, jbyteArray jdata, jint data_len,jbyteArray jout_crc){

    jbyte *jpszData = (*env)->GetByteArrayElements(env, jdata, 0);

    jbyte *outData = (*env)->GetByteArrayElements(env, jout_crc, 0);

    uint16_t result=CalcCRC(jpszData,data_len);

    outData[0]=result>>8;

    outData[1]=result&0xFF;

    (*env)->ReleaseByteArrayElements(env, jdata , jpszData, 0);

    (*env)->ReleaseByteArrayElements(env, jout_crc , outData, 0);

    return 0;

}


ModuleAPI.java

package com.xlzn.hcpda;

public class ModuleAPI {

    private static ModuleAPI moduleAPI=new ModuleAPI();

    private ModuleAPI(){}


    public static ModuleAPI getInstance(){

        return moduleAPI;

    }

    static {

        System.loadLibrary("ModuleAPI");

    }

    public native int SerailOpen(String uart, int baudrate, int databits, int stopbits, int parity);

    public native int SerailClose(int uart_fd);

    public native int SerailSendData(int uart_fd,byte[] sendData,int sendLen);

    public native int SerailReceive(int uart_fd,byte[] receiveData,int receiveDataLen);

    public native int CalcCRC(byte[] data,int dataLen,byte[] outCrc);


    public static int getVersionCode = BuildConfig.API_VERSION;




}


1.png

2.png


3.png

build.gradle设置相关内容:

plugins {

    id 'com.android.library'

}


android {

    compileSdkVersion 31

    buildToolsVersion "30.0.3"


    defaultConfig {

        minSdkVersion 21

        targetSdkVersion 31

        versionCode 16

        versionName "2.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        consumerProguardFiles "consumer-rules.pro"

        externalNativeBuild {

            cmake {

                abiFilters "armeabi-v7a" //abi体系结构下的so库

            }

        }

    }


    signingConfigs {

        debug {

            File strFile = new File("/android.keystore")

            storeFile file(strFile)

            storePassword "123456"

            keyPassword "123456"

            keyAlias "keyAlias"

        }

        release {

            File strFile = new File("/release.jks")

            storeFile file(strFile)

            storePassword "123456"

            keyPassword "123456"

            keyAlias "keyAlias"

        }

    }



    buildTypes {

        release {

            signingConfig signingConfigs.release

            minifyEnabled false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            android.libraryVariants.all { variant ->

                variant.outputs.all {

                    outputFileName = "HCUHF_${versionCodeA()}_${releaseTime()}.aar"

                }

            }

            buildConfigField("int", "API_VERSION", "${releaseTime()}")

        }

        debug {

            signingConfig signingConfigs.debug

            minifyEnabled false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            buildConfigField("int", "API_VERSION", "${releaseTime()}")

        }

    }

    sourceSets {

        main {

            jni.srcDirs = ['src/main/jni']

            jniLibs.srcDirs = ['src/main/jni/libs']

            java.srcDirs = ['src/main/java']

        }

    }


    externalNativeBuild {

        cmake {

            path file('src/main/jni/CMakeLists.txt')

        }

    }


    compileOptions {

        sourceCompatibility JavaVersion.VERSION_1_8

        targetCompatibility JavaVersion.VERSION_1_8

    }

}


dependencies {

    implementation 'androidx.appcompat:appcompat:1.1.0'

    implementation 'com.google.android.material:material:1.1.0'


    implementation files('libs\\classes.jar')


    implementation files('libs\\jxl.jar')

    implementation files('libs\\xUtils-2.5.5.jar')

    implementation files('libs\\classes.jar')


}


static def releaseTime() {

    new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08:00"))

}


static def versionName() {

    "\"v1.0.7\""

}


static def versionCodeA() {

    "v1.0.7"

}


CMakeLists.txt内容

cmake_minimum_required (VERSION 3.4.1)

project(ModuleAPI)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})

include_directories("${CMAKE_CURRENT_SOURCE_DIR}/inc")

file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c"

        )

add_library(

ModuleAPI

SHARED

        ${SRC_LIST}

)


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 )


# Specifies libraries CMake should link to your target library. You

# can link multiple libraries, such as libraries you define in this

# build script, prebuilt third-party libraries, or system libraries.


target_link_libraries( # Specifies the target library.

        ModuleAPI


        # Links the target library to the log library

        # included in the NDK.

        ${log-lib} )


数据类型对照

1.png



Android打aar没有包含so的解决办法

引言

在Android开发中,我们常常会将一些功能封装成库文件(aar)进行使用。然而有时候在打aar包时,可能会遇到一种情况:aar文件中没有包含.so文件。这时候我们需要告诉刚入行的小白如何解决这个问题。

解决方案概述

解决这个问题的基本思路是,在打aar包时将.so文件也打包进去。下面是整个解决方案的流程图:

1.png

开始

创建Android Library工程

在工程的main目录下创建jniLibs目录

将.so文件复制到jniLibs目录下

在工程的build.gradle文件中添加ndk配置

编译并打包aar

结束

下面我们将逐步解释每一步需要做什么。


步骤详解

1. 创建Android Library工程

首先,在Android Studio中创建一个新的Android Library工程。


2. 创建jniLibs目录

在工程的main目录下创建一个名为jniLibs的目录。这个目录将用于存放.so文件。


3. 复制.so文件

将需要打包的.so文件复制到jniLibs目录下。注意,如果有多个.so文件,都需要复制到该目录下。


4. 添加ndk配置

在工程的build.gradle文件中添加ndk的配置。具体的代码如下所示:


android {

    ...

    sourceSets {

        main {

            jniLibs.srcDirs = ['src/main/jniLibs']

        }

    }

    ...

}

这段代码的作用是告诉Android Studio,要将jniLibs目录作为so库的来源。


5. 编译并打包aar

最后,编译并打包aar。可以通过在终端执行以下命令来实现:

./gradlew assembleRelease

这将在工程的build/outputs/aar目录下生成一个aar文件。

至此,我们完成了解决方案的所有步骤。


总结

本文介绍了解决Android打aar没有包含.so文件的问题的详细步骤。通过创建jniLibs目录,将.so文件复制到该目录下,并在build.gradle文件中添加ndk配置,最终成功编译并打包出包含.so文件的aar。希望本文对刚入行的小白有所帮助。

(以上为所需代码,请用“行内代码”的markdown语法标识出来)

android {

    ...

    sourceSets {

        main {

            jniLibs.srcDirs = ['src/main/jniLibs']

        }

    }

    ...

}


./gradlew assembleRelease














上一篇:Android快速回顾

下一篇:Android同行者

Top