Android-JNITest

Introduction: 一个 Demo 告诉你怎么搞 JNI
More: Author   ReportBugs   
Tags:
JNI-NDK-

Android Studio JNI environment.
Android Studio JNI 环境配置。


介绍

Android Studio + NDK 来实现 JNI。

什么是 NDK 与 JNI 技术?
NDK:Native Development Kit

The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++.(谷歌官方文档)

大致意思:NDK 是一个工具,可以让你实现你的应用程序使用本地代码的语言,如 C 和 C++的部分。

JNI:Java Native Interface
它提供了若干的 API 实现了 Java 和其他语言的通信(主要是 C&C++)。从 Java1.1 开始,JNI 标准成为 java 平台的一部分,它允许 Java 代码和其他语言写的代码进行交互。

准备工作

  • 1.搭建好 Android Studio 开发环境。
  • 2.新建一个 Android 项目

Android Studio 配置 NDK

  • 1.如图所示下载 LLDB+NDK 并安装。
    第一

  • 2.配置安装好的 NDK 路径。
    第二

  • 3.配置一些快捷方式。
    第三
    javah
    ndk-build 三
    ndk-build clean

      javah    用于生成头文件
      Program:$JDKPath$/bin/javah
      注意:这个命令我加上了-encoding UTF-8 指定编码,你可以改成你工程的编码。
      Parameters:-encoding UTF-8 -d ../jni -jni $FileClass$
      Working directory:$SourcepathEntry$\..\java
    
      ndk-build    用于构建 so 包
      注意:MAC/Linux 用 ndk-build,没有.cmd 后缀
      Program:C:\Develop\Android\sdk\ndk-bundle\ndk-build.cmd
      Parameters:什么都不用填
      Working directory:$ModuleFileDir$\src\main
    
      ndk-build clean    清除 so 包
      注意:MAC/Linux 用 ndk-build,没有.cmd 后缀
      Program:C:\Develop\Android\sdk\ndk-bundle\ndk-build.cmd
      Parameters:clean
      Working directory:$ModuleFileDir$\src\main
    

配置项目

  • 1.修改根目录下的 build.gradle

      buildscript {
          repositories {
          jcenter()
          }
          dependencies {
          //    修改 build:gradle 为 build:gradle-experimental
              classpath "com.android.tools.build:gradle-experimental:0.7.0"
          //        classpath 'com.android.tools.build:gradle:2.1.2'
          }
      }
    
      allprojects {
          repositories {
              jcenter()
          }
      }
      //添加
      task clean(type: Delete) {
          delete rootProject.buildDir
      }
    
  • 2.修改 gradle->wrapper->gradle-wrapper.properties

      #Mon Dec 28 10:00:20 PST 2015
      distributionBase=GRADLE_USER_HOME
      distributionPath=wrapper/dists
      zipStoreBase=GRADLE_USER_HOME
      zipStorePath=wrapper/dists
      distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip//修改这里的版本号
    
  • gradle-experimental 与 gradle-wrapper 相对应的版本号如下图
    版本号对比

  • 3.修改 app->build.gradle

      修改之前的
      apply plugin: 'com.android.application'
    
      android {
          compileSdkVersion 23
          buildToolsVersion "23.0.3"
    
          defaultConfig {
              applicationId "com.jeanboy.demo.jnitest"
              minSdkVersion 15
              targetSdkVersion 23
              versionCode 1
              versionName "1.0"
          }
          buildTypes {
              release {
                  minifyEnabled false
                  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
              }
          }
      }
    
      dependencies {
          compile fileTree(dir: 'libs', include: ['*.jar'])
          compile 'com.android.support:appcompat-v7:23.4.0'
      }
    
      修改之后的
      apply plugin: 'com.android.model.application'//修改
      //apply plugin: 'com.android.application'
    
      model {//修改
          android {
              compileSdkVersion 23
              buildToolsVersion "23.0.3"
    
              defaultConfig {
                  applicationId "com.jeanboy.demo.jnitest"
                  minSdkVersion.apiLevel 15
                  targetSdkVersion.apiLevel 23
                  versionCode   1
                  versionName   "1.0"
              }
    
              ndk {//指定生成的 lib,比如此时生成 native.so
                  moduleName   "NdkTest"
              }
    
              buildTypes {
                  release {
                      minifyEnabled false
                      proguardFiles.add(file("proguard-rules.pro"))//修改
                  }
              }
    
          }
    
      }
    
      dependencies {
          compile fileTree(dir: 'libs', include: ['*.jar'])
          compile 'com.android.support:appcompat-v7:23.4.0'
      }
    
  • 4.创建 jni 文件夹
    创建 jni 文件夹

  • 5.创建 NdkTest.java

      public class NdkTest {
          static {
              System.loadLibrary("NdkTest");//加载要使用的 so 文件
          }
          //生命 native 方法
          public static native String getString();
          public static native int doAdd(int param1,int param2);
      }
    
  • 6.生成 NdkTest.h 并创建 NdkTest.cpp 实现 NdkTest.h 中的 native 方法
    生成 NdkTest.h

NdkTest.h 文件内容

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

    #ifndef _Included_com_jeanboy_demo_jnitest_NdkTest
    #define _Included_com_jeanboy_demo_jnitest_NdkTest
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_jeanboy_demo_jnitest_NdkTest
     * Method:    getString
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_jeanboy_demo_jnitest_NdkTest_getString
      (JNIEnv *, jclass);//待实现的 native 方法

    /*
     * Class:     com_jeanboy_demo_jnitest_NdkTest
     * Method:    doAdd
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_jeanboy_demo_jnitest_NdkTest_doAdd
      (JNIEnv *, jclass, jint, jint);//待实现的 native 方法

    #ifdef __cplusplus
    }
    #endif
    #endif

NdkTest.cpp 文件内容

    #include "com_jeanboy_demo_jnitest_NdkTest.h"

    JNIEXPORT jstring JNICALL Java_com_jeanboy_demo_jnitest_NdkTest_getString
            (JNIEnv *env, jclass type) {//具体实现

        return env->NewStringUTF("hello world!!!");
    }

    /*
     * Class:     com_jeanboy_demo_jnitest_NdkTest
     * Method:    doAdd
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_jeanboy_demo_jnitest_NdkTest_doAdd
            (JNIEnv *env, jclass type, jint param1, jint param2) {//具体实现

        return param1 + param1;
    }
  • 7.在 jni 文件夹下创建 Android.mk 和 Application.mk

Android.mk 文件内容

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

    LOCAL_MODULE := NdkTest//moduleName
    LOCAL_SRC_FILES := NdkTest.cpp//上面创建的 NdkTest.cpp
    include $(BUILD_SHARED_LIBRARY)

Application.mk 文件内容

    APP_MODULES := NdkTest
    /*这个变量是可选的,如果没有定义,NDK 将由在 Android.mk 中声明的默认的模块编译,并且包含所有的子文件(makefile 文件);
    如果 APP_MODULES 定义了,它不许是一个空格分隔的模块列表,这个模块名字被定义在 Android.mk 文件中的 LOCAL_MODULE 中。
    注意 NDK 会自动计算模块的依赖*/
    APP_ABI := all//支持所有平台,也可以指定平台空格隔开 armeabi armeabi-v7a x86
    Android 系统目前支持的 CPU 架构:

    ARMv5,ARMv7 (从 2010 年起)
    x86 (从 2011 年起)
    MIPS (从 2012 年起)
    ARMv8,MIPS64 和 x86_64 (从 2014 年起)

    每一个 CPU 架构对应一个 ABI
    CPU 架构            ABI
    ARMv5    --->    armeabi
    ARMv7    --->    armeabi-v7a
    x86        --->    x86
    MIPS    --->    mips
    ARMv8    --->    arm64-v8a
    MIPS64    --->    mips64
    x86_64    --->    x86_64

    armeabi:默认选项,将创建以基于 ARM* v5TE 的设备为目标的库。 具有这种目标
    的浮点运算使用软件浮点运算。 使用此 ABI(二进制接口)创建的二进制代码将可以
    在所有 ARM*设备上运行。所以 armeabi 通用性很强。但是速度慢

    armeabi-v7a:创建支持基于 ARM* v7 的设备的库,并将使用硬件 FPU 指令。
    armeabi-v7a 是针对有浮点运算或高级扩展功能的 arm v7 cpu。

    mips:MIPS 是世界上很流行的一种 RISC 处理器。MIPS 的意思是“无内部互锁流水级
    的微处理器”(Microprocessor without interlocked piped stages),其机
    制是尽量利用软件办法避免流水线中的数据相关问题。

    x86:支持基于硬件的浮点运算的 IA-32 指令集。x86 是可以兼容 armeabi 平台运行
    的,无论是 armeabi-v7a 还是 armeabi,同时带来的也是性能上的损耗,另外需要
    指出的是,打包出的 x86 的 so,总会比 armeabi 平台的体积更小。

    总结
    如果项目只包含了 armeabi,那么在所有 Android 设备都可以运行;
    如果项目只包含了 armeabi-v7a,除 armeabi 架构的设备外都可以运行; 
    如果项目只包含了 x86,那么 armeabi 架构和 armeabi-v7a 的 Android 设备是无法
    运行的;
    如果同时包含了 armeabi,armeabi-v7a 和 x86,所有设备都可以运行,程序在运
    行的时候去加载不同平台对应的 so,这是较为完美的一种解决方案,同时也会导致
    包变大。
  • 8.生成 so 文件
    生成 so 文件

  • 9.在需要 native 方法的地方直接调用

      NdkTest.getString();
      NdkTest.doAdd(5, 12);
    
  • 10.运行 app 试试效果吧

关于我

如果对你有帮助,请 star 一下,然后 follow 我,给我增加一下分享动力,谢谢!

如果你有什么疑问或者问题,可以提交 issue 和 request,发邮件给我 jeanboy@foxmail.com 。

或者加入下面的 QQ 群来一起学习交流。

Android 技术进阶:386463747

License

Copyright 2015 jeanboy

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.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools