zoukankan      html  css  js  c++  java
  • [Android] AndroidStudio + JNI(NDK)开发相关总结

    1、官方推荐JNI构建方案

    从Android studio 2.2 开始,Google推荐的JNI开发构建工具是CMake而不是NDK,参考官方文档:https://developer.android.com/studio/projects/add-native-code.html

    CMake的编译配置参数参考一篇文章https://www.jianshu.com/p/6332418b12b1

    一个典型的使用CMake的Gradle配置如下:

     1 defaultConfig {
     2     minSdkVersion 14                 // 指定支持最低的API版本号,如果不指定就是支持所有的版本,这有可能导致某些高级语法函数不支持等
     3     targetSdkVersion 23             // 指定目前API版本,这个是主要编译版本,推荐与compileSdkVersion版本保持一致
     4     versionCode 1
     5     versionName "1.0"
     6 
     7     externalNativeBuild {            // 必要第(1)点,指定默认编译参数,cmake表示特指CMake工具链的参数
     8         cmake {
     9             cppFlags "-frtti -fexceptions"         // 支持dynamic_cast与try等
    10             abiFilters 'armeabi-v7a'                // 指定编译架构
    11         }
    12     }
    13 }
    14 
    15 buildTypes {
    16     debug {
    17         jniDebuggable true
    18     }
    19 }
    20 
    21 externalNativeBuild {            // 必要第(2)点,指定CMake文件,也就是编译内容
    22     cmake {
    23         path "jni\build\android\armeabi-v7a\debug\CMakeLists.txt"
    24     }
    25 }
    View Code

    Gradle编译的版本配置相关:https://www.jianshu.com/p/3645a7829502

    但是,我们要求使用NDK-r9d,默认不携带CMake,会报错提示找不到make工具等。

    关于Gradle插件NDK相关的用法,参考官方文档:http://tools.android.com/tech-docs/new-build-system/gradle-experimental

    2、JNI构建过程

    基本流程是:(1)在AS中配置好NDK路径;(2)创建Java工程,然后写一个Java类中声明Native方法;(3)编译一下Java类生成class;(4)用javah -d ../jni com.example.kuliuheng.myapplication.TestClass 就能自动生成native方法对应的C++头文件;(5)就在jni目录下去添加cpp文件或者c文件去实现;(6)如果用Gradle自动编译,那么在android.defaultConfig下添加一个ndk模块就可以了,写一个moduleName就行。

    3、新的NDK工程构建方法

      上面描述的是AndroidStudio自带的NDK工程构建步骤,这里描述一种利用Experimental plugin 很方便地构建NDK工程的方法。在此之前,先搞清楚几个名词的关系:

    3.1 Gradle 与 Android Plugin(for Gradle)

      首先来看下一个默认工程中这两者的关系:

      这两者什么关系呢?可以类比的理解为Gradle(包)是一个服务方,而AndroidPlugin是一个客户端,也叫作Gradle插件。这两个东东都是Android Studio自带默认就有的,可以说默认就是这两个在起作用。

    <1> AndroidPlugin(Gradle插件)配置在AS工程的build.gradle中

    <2> Gradle包配置在gradle-wrapper.properties 中

      当然也可以在工程设置中更改,指定为最新的或者自己下载好本地的Gradle包:

    <3> AndroidPlugin(Gradle插件)的版本要求

      先看下官方文档对AndroidPlugin(for Gradle)的描述吧。可以看到这两者是有版本对应关系的:

    Plugin versionRequired Gradle version
    1.0.0 - 1.1.3 2.2.1 - 2.3
    1.2.0 - 1.3.1 2.2.1 - 2.9
    1.5.0 2.2.1 - 2.13
    2.0.0 - 2.1.2 2.10 - 2.13
    2.1.3 - 2.2.3 2.14.1+
    2.3.0+ 3.3+

     3.2 Gradle 与 Gradle-Experimental plugin

      如果是用上面的两个东东来开发NDK呢,那就是前面所描述的那种比较复杂的JNI开发方法,还是比较麻烦的。这就引出了主角Gradle-Experimental plugin了,先看看官方文档的介绍。

    其中,最开始的地方就给出了一个特别的说明:

    CAVEAT: Note that this plugin is a preview of the plugin for feedback on performance and NDK integration.  The Gradle API for the new component model is not final, which means each plugin will only work with a specific version of Gradle.
    Additionally, the DSL may also change.

        意思是说这个插件是为了预览体验性能和NDK集成开发的,它的API都不是最终版本,每一个版本的Gradle只能对应一个特定版本的插件。看看版本对应关系:

     Plugin Version  Gradle Version
     0.1.0  2.5
     0.2.0  2.5
     0.3.0-alpha3  2.6
     0.4.0  2.8
     0.6.0-alpha1  2.8
     0.6.0-alpha5  2.10
     0.7.0-alpha1  2.10 
     0.7.0  2.10
     0.7.3  2.14.1

      看官方文档上给的对应关系说明,好像是有点“跟不上进度”的样子,现在Gradle都到3.3版本了,这个插件才写了2.14.1的对应版本。不过,当配置好上图中的一个之后,AndroidStudio会自动提示升级,我这里自动升级到了Plugin 0.9.3 <--> Gradle 3.3

    3.3 最重要的一节(怎么使用Gradle-Experimental plugin)

    从传统的Android Gradle Plugin迁移到Experimental Plugin,需要進行一下的配置调整(下方的配置以Gradle Version为2.10的为例子,其他版本的配置类似)
    .
    ├── app/
    │   ├── app.iml
    │   ├── build.gradle
    │   └── src/
    ├── build.gradle
    ├── gradle/
    │   └── wrapper/
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradle.properties
    ├── gradlew*
    ├── gradlew.bat
    ├── local.properties
    ├── MyApplication.iml
    └── settings.gradle

      上方的目录视图显示了一个典型的AS项目的结构,这里一共有三个文件需要修改:

    <1> gradle-wrapper.properties 配置Gradle版本

    <2> 配置工程build.gradle

     

      这个时候不要急于的同步工程,一来是因为还没配置完,二来是使用了这个Experimental plugin之后很多语法会发生改变,所以还修改Gradle内部配置。此时同步工程会报错,说一些方法、包什么的找不到。

    <3> 配置模块内部的build.gradle

      先给一个典型的配置吧,里面配置NDK的方法我看很实用,比如更改代码目录名、增加引用库关系等等都可以:

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
    //        classpath 'com.android.tools.build:gradle:2.1.3'
            classpath 'com.android.tools.build:gradle-experimental:0.8.3'
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    apply plugin: 'com.android.model.application'
    
    model {
        android {
            compileSdkVersion = 23
            buildToolsVersion = '23.0.3'
    
            defaultConfig {
                applicationId "com.gala.uniplayer.androiddemotest"
                minSdkVersion.apiLevel 14
                targetSdkVersion.apiLevel 24
                versionCode 1
                versionName "1.0"
                multiDexEnabled true
            }
    
            buildTypes {
                release {
                    minifyEnabled = false
                    proguardFiles.add(file("proguard-rules.pro"))
                }
            }
    
            ndk {
                platformVersion 19
                moduleName "galauniplayersdkjni"
                CFlags.add("")
                cppFlags.addAll(["-std=c++11", "-frtti"])
                cppFlags.addAll(['-I' + file('../../../../../uniplayersdk-pub/include/uniplayersdk'),
                                 '-I' + file('../../../../../qyuniutil-pub/include'),
                                 '-I' + file('src/main/cpp/include'),
                                ])
    
                abiFilters.add("armeabi")
                CFlags.add("-DCUSTOM_DEFINE")
                cppFlags.add("-DCUSTOM_DEFINE")
    
                File curDir = file('./')
                curDir = file(curDir.absolutePath)
                String libsDir = curDir.absolutePath + '/src/main/libs/armeabi'
                String rootPath = curDir.absolutePath +"/../../../../../"
    
                ldFlags.add("-L" + libsDir)
                ldFlags.add("-L" + rootPath+"qyuniutil-pub/libs/android/armeabi")
                ldFlags.add("-L" + rootPath+"uniplayersdk-pub/libs/android/armeabi")
    
                ldLibs.add("mcto_media_player")
                ldLibs.add("galauniutil")
                ldLibs.add("galauniplayersdk")
    
                stl "gnustl_shared"
            }
    
            sources {
                main {
                    java {
                        source {
                            srcDir "src/main/java"
                        }
                    }
                    jni {
                        source {
                            srcDir "src/main/cpp"
                        }
                    }
                    jniLibs {
                        source {
                            srcDir "src/main/libs"
                        }
                    }
                }
            }
        }
    }
    
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'src/main/libs')
        compile files('src/main/libs/cupid_app_interface.jar')
        compile files('src/main/libs/nativemediaplayersdk.jar')
        compile files('src/main/libs/galauniplayersdk.jar')
    //    compile project(':uniplayer_lib')
    }
    
    
    task copySoTask(type: Copy) {
        from('../../../../../thirdparty-libs/libs/android/tvjson/shared/') {
            include 'armeabi/*.so'
        }
        from('../../../../../thirdparty-libs/libs/android/puma_tv/shared/') {
            include 'armeabi/*.so'
        }
        from('../../../../../thirdparty-libs/libs/android/puma_tv/jar/') {
            include '*.jar'
        }
        from('../../../../../uniplayerdata-pub/libs/android/') {
            include 'armeabi/*.so'
        }
        from('../../../../../uniplayersdk-pub/libs/android/') {
            include 'armeabi/*.so'
        }
        from('../../../../../uniplayersdk-pub/libs/android/') {
            include 'galauniplayersdk.jar'
        }
        from('../../../../../qyuniutil-pub/libs/android/') {
            include 'armeabi/*.so'
        }
        into 'src/main/libs'
    }
    
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn copySoTask
    }
    View Code

        使用了这个插件之后带来语法上的变化主要有以下几点:

    1. Plugin name is com.android.model.application instead of com.android.application. Or use apply plugin: “com.android.model.library” if you want to create an Android aar library. 
      使用com.android.model.application代替com.android.application;  使用com.android.model.library代替com.android.library
    2. Configuration is wrapped with the model { } block 
      所有的配置需要使用model{}包含起来
    3. Adding elements to a Collection should be done using the add method. 
      向集合添加元素时,需要使用add方法

      上图是官方文档给出来的需要变更语法的地方。

    <4> 官方文档很重要

      好多NDK配置特性都能找到:http://tools.android.com/tech-docs/new-build-system/gradle-experimental

      一个歪果大牛写的入门Blog:https://ph0b.com/new-android-studio-ndk-support/

    4、JNI类型映射

    Java 类型                    

    符号

    Boolean

    Z

    Byte

    B

    Char

    C

    Short

    S

    Int

    I

    Long

    J

    Float

    F

    Double

    D

    Void

    V

    objects对象

    以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用$来表示嵌套。例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"

  • 相关阅读:
    Flink安装排坑指南
    Java对类进行XSS过滤
    java.sql.SQLException: 无法转换为内部表示
    Flutter环境搭建
    运行npm命令 TypeError: Cannot read property 'get' of undefined
    利用Xilinx的cordic ip做开方运算
    modelsim脚本化仿真出现的两个错误
    opencv中使用imread()读入的图像,始终为显示为8位深度
    PS端时钟供给PL使用的问题
    Xilinx 使用shift_RAM消耗分布式RAM(LUTRAM)还是BRAM (Block RAM)?
  • 原文地址:https://www.cnblogs.com/kuliuheng/p/6891887.html
Copyright © 2011-2022 走看看