NDK使用

作者: 主音King | 来源:发表于2019-11-14 13:01 被阅读0次

    基于Android Studio3.5

    user:纯粹.so连接库使用者
    creator:纯粹ndk开发者,创建.so
    designer:在现有的.so进行开发.so连接库特定功能

    纯粹.so连接库使用者
    在main下新建jniLibs,是默认的.so放置。创建者提供JNI接口,JNI是根据包名找到C/C++函数,使用时候必须和creator定义的接口完全一致(包括包名)

    -jniLibs
      -arm64-v8a
        xx.so
      -armeabi-v7a
        xx.so
      ....
    

    JNI接口实现有两种java或者kotlin如下

    package cn.george.nativecreator;// 包名一定要和so中提供的一致
    /**
     * Created By George
     * Description: java实现JNI
     */
    public class MainActivity {
        static {
            // 注意不是libnative-lib.so
            System.loadLibrary("native-lib");
        }
        public static native String  stringFromJNI(String top, String bottom, String brow, String eyes);
    }
    
    
    package cn.george.nativecreator// 包名一定要和so中提供的一致
    /**
     * Created By George
     * Description:kotlin实现JNI
     */
    class MainActivity {
        companion object{
            init {
                System.loadLibrary("native-lib")
            }
        }
        external fun stringFromJNI( top:String, bottom:String ,brow: String , eyes:String ):String
    }
    

    注意:
    如果只用一个.so库,比如:项目中之支持armeabi-v7a,则需要在app的build中添加:

    android{
     defaultConfig{
     ...
     ndk {
            abiFilters 'armeabi-v7a'
            }
     }
    }
     ....
    }
    

    纯粹ndk开发者,创建.so
    在Android Studio中需要创建一个Native C++的Android项目,会自动生成

    -main
      -cpp
        CmakeLists.txt
        xxx.cpp
    

    CmakeLists用来配置加入的.h .cpp文件,全局变量等
    MainActivity中会自动生成JNI的native接口方法和C++实现

    arm 架构注重的是续航能力:大部分的移动设备
    x86 注重性能:大部分台式机和笔记本电脑
    可以在build中控制.so输出种类

    android {
        defaultConfig {
            externalNativeBuild {
                cmake {
                    abiFilters 'armeabi-v7a', 'arm64-v8a'
                }
            }
        }
    

    .so默认是生成在app/build/intermediates/cmake下分为debug和release
    在build中也可以自定义.so的输出位置

    android {
        ...
        sourceSets {
            main {
                jniLibs.srcDirs = ['target']//指定so库的位置,加载so库
            }
        }
    }
    

    arm64-v8a: 第8代、64位ARM处理器
    armeabi-v7a :第7代及以上的 ARM处理器
    x86: x86架构的CPU(Intel CPU)
    x86_64: x86架构的64位CPU(Intel的CPU)

    .so .dll .dylib都是由C++实现的。
    .so用在linux上,.dll用在windows上,.dylib用在MacOS中。
    他们都是C++的动态链接库(Dynamic Link Library)

    注意:
    如果在写C/C++代码确认自己写的没问题,但是飘红Build-->Refresh Linked C++ Projects
    通过Build-->Make Project生成.so

    在现有的.so进行开发.so连接库特定功能
    比如FFmpeg和OpenCV都是这种实现。
    同样你需要创建Native C++项目作为第三方源码库,可以添加一些基本操作,比如加,减,乘,除

    extern "C" int add(int a, int b) {
        return a + b;
    }
    

    然后生成three-lib.so库作为第三方库。
    同样要创建Native C++项目作为自定义第三方库的项目。
    把第三方库three-lib.so放到这个项目的jniLibs/支持的cpu架构/下
    然后对CMakeLists.txt处理

    cmake_minimum_required(VERSION 3.4.1)
    
    # 第三方so的名字注意不是libthree-lib,动态库标志,导入第三方标志
    add_library(three-lib SHARED IMPORTED)
    # 这两个属性必须靠在一起,下边是对上边的解释;第三方so名字,设置so的路径armeabi-v7a等
    set_target_properties(three-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libthree-lib.so)
    
    # 自定义jni的名字,动态库标示,jni源文件
    add_library(native-lib SHARED native-lib.cpp)
    # 配置include路径(如果需要使用.h文件的话),这里没有用到,可以去掉
    target_include_directories(native-lib PRIVATE ${CMAKE_SOURCE_DIR}/../myinclude)
    
    find_library(log-lib log)
    
    # 链接所有的.cpp .so,(.h文件不需要),第三方so的名称
    target_link_libraries(native-lib three-lib ${log-lib})
    

    在自定义native-lib.cpp中进行代码调用第三方库

    extern "C" JNIEXPORT int JNICALL
    Java_cn_george_nativedesigner_MainActivity_add(
            JNIEnv *env,
            jobject /* this */,jint a,jint b) {
        LOGD("加法");
        return add(a,b);
    }
    

    在MainActivity添加add方法(kotlin)

        external fun divi(a:Int,b:Int):Int
    
        companion object {
    
            // Used to load the 'native-lib' library on application startup.
            init {
                System.loadLibrary("native-lib")
            }
        }
    

    然后可以调用java的jni add进行加法运算。
    注意:
    如果想在native打印日志需要在build中引入日志库

    android{
    ...
      defaultConfig{
    ...
    ndk{
                moduleName "Iso8583Lib"// 日志相关编码
                ldLibs "log", "z", "m"// 日志相关
                abiFilters 'armeabi-v7a'// 只引用指定特定cpu架构
    }
    externalNativeBuild {
                cmake {
                    cppFlags ""
                    abiFilters 'armeabi-v7a'// 产出指定的cpu架构
                }
            }
    }
    }
    

    注意:
    在混合编码c、ndk、java、jni时候需要注意变量类型的转化

    Java_cn_george_nativedesigner_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    

    所有的本地方法的第一个参数都是指向JNIEnv结构的。这个结构是用来调用JNI函数的。第二个参数jobject的意义,要看方法是不是静态的(static)或者(Instance)的。前者代表一个类对象的引用,后者被调用的方法所属对象的引用。返回值和参数类型根据等价约定映射本地C/C++类型,如下JNI类型映射表


    JNI类型映射表

    相关文章

      网友评论

          本文标题:NDK使用

          本文链接:https://www.haomeiwen.com/subject/crqhictx.html