Android从头再来,JNI学习总结

2020-03-14 08:10 来源:未知

2.1 简介

  • 定义:Native Development Kit,是 Android的一个工具开发包

NDK是属于 Android 的,与Java并无直接关系

  • 作用:快速开发CC 的动态库,并自动将so和应用一起打包成 APK

即可通过 NDKAndroid中 使用 JNI与本地代码交互

  • 应用场景:在Android的场景下 使用JNI

Android开发的功能需要本地代码实现

  • 特点

美高梅网投网址 1示意图

  • 额外注意

美高梅网投网址 2示意图

举例子

1.配置DNK环境

For exlipse

android Studio 2.2 and later already integrated NDK环境

2.创建 Android 项目

3.在 Android 项目中声明所需要调用的 Native方法

4.使用 NDK 创建 Java 本地库

4.1.在项目文件下创建一个名为 jni 的新文件夹。

4.2.在 jni 文件夹内,创建名为 Android.mk 的文件,该文件包含正确构建和命名库的 makefile 说明。

4.3.在 jni 文件夹内,创建源文件,会在 Android.mk 文件中引用该源文件以PhotoAnalyzer为例子,jni目录下有如下文件:

Android.mk

Application.mk

PhotoAnalyzer_onload.cpp

PhotoAnalyzerWrapper.cpp

Android.mk 文件内容解释

LOCAL_PATH := $(call my-dir)

// 设置工作目录,而my-dir则会返回Android.mk文件所在的目录include $(CLEAR_VARS)

// 清除几乎所有以LOCAL——PATH开头的变量(不包括LOCAL_PATH)LOCAL_MODULE := libPhotoAnalyzerjni

// 设置模块的名称,即编译出来.so文件名。共享库命名约定为 lib.so

LOCAL_SRC_FILES :=

    PhotoAnalyzer_onload.cpp

    PhotoAnalyzerWrapper.cpp

// 指定参与模块编译的C/C 源文件名

include $(BUILD_SHARED_LIBRARY)

// 指定生成的静态库或者共享库在运行时依赖的共享库模块列表。

Application.mk内容解释

作用:配置编译平台相关内容

具体使用

APP_ABI := armeabi

// 最常用的APP_ABI字段:指定需要基于哪些CPU平台的.so文件

// 常见的平台有armeabi x86 mips,其中移动设备主要是armeabi平台

// 默认情况下,Android平台会生成所有平台的.so文件,即同APP_ABI := armeabi x86 mips

// 指定CPU平台类型后,就只会生成该平台的.so文件,即上述语句只会生成armeabi平台的.so文件

5.通过 ndk - bulid 命令编译产生.so库文件

打开终端,输入以下命令

1.run ndk-build clean

2.run ndk-build

3.check output file libPhotoanalyzer/src/photoanalyzer/src/libs/armeabi-v7a/libPhotoanalyzer.so

编译成功后,在src/main/会多了两个文件夹libs & obj,其中libs下存放的是.so库文件

在project中创建一个名为libs的文件夹,并将上述生成的so文件夹放到该目录下

要把名为 CPU平台的文件夹放进去,而不是把.so文件放进去armeabi/libPhotoanalyzer.so

6.在Andoird Studio项目中使用NDK实现JNI功能

public class MainActivity extends AppCompatActivity  {

    // 步骤1:加载生成的so库文件

    // 注意要跟.so库文件名相同

美高梅网投网址,    static {

        System.loadLibrary("libPhotoanalyzer");

    }

    // 步骤2:定义在JNI中实现的方法

    public native String getXXXXX();

    // 此处设置了一个按钮用于触发JNI方法

    private Button Button;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        // 通过Button调用JNI中的方法

        Button = (Button) findViewById(R.id.button);

        Button.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Button.setText(getXXXXX());

            }

        });

    }

附图project目录结构:

美高梅网投网址 3

Android Studio 2.2 以上版本

只需要在Android Studio中的SDK tools中下载Cmake、NDK和LLDB三个工具,然后在Android Studio中新建项目的时候,在向导界面勾选C 支持选项然后创建项目就可以了。

美高梅网投网址 4

勾选支持C 选项

项目创建好以后我们可以看到和普通Android项目有以下4个不同。

  • main 下面增加了 cpp 目录,即放置 c/c 代码的地方
  • module的 build.gradle 有修改
  • 增加了 CMakeLists.txt 文件
  • 多了一个 .externalNativeBuild 目录

美高梅网投网址 5

项目目录

详细内容可以参看Android NDK开发扫盲及最新CMake的编译使用。
由此可以总结出使用CMake编程的步骤如下:

  • 新建cpp目录,写好C/C 代码。
  • 创建并配置CMakeLists.txt文件。
  • build.gradle文件中根据情况进行配置,CMakeLists.txt文件的路径必须配置。
  • java代码中即可调用C/C 代码,运行程序。Java中调用JNI方法需要在具体的类中做以下操作:
//1、加载lib库
// Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

//2、声明调用的方法
/**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

//3、调用方法
// Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

运行问题

  1. JNI层没有及时DetachCurrentThread(g_jvm->DetachCurrentThread())导致内存泄漏Crash

     class JNIEnvGuard {
     public:
         JNIEnvGuard() {
             int status;
             needDetachEnv = false;
             status = g_jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
             if(status < 0){
                 if (g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
                     JNI_LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
                     return;
                 }
                 needDetachEnv = true;
             }
         }
    
     virtual ~JNIEnvGuard() {
         if (needDetachEnv) {
             if (g_jvm->DetachCurrentThread() != JNI_OK) {
                 JNI_LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
             }
         }
     }
    
     JNIEnv *getEnv() {
         return env;
     }
    
     private:
         JNIEnv *env;
         bool needDetachEnv;
     };
    
     通过这个类自动析构线程
    
  2. (g_jvm)->AttachCurrentThread(&env, NULL) 后使用 (g_jvm)->DetachCurrentThread();程序报错

     调用DetachCurrentThread函数的地方在java线程中,
     即在java调用C  代码时在C  代码中调用了AttachCurrentThread方法来获取JNIEnv,
     此时JNIEnv已经通过参数传递进来
     你不需要再次AttachCurrentThread来获取。在释放时就会报错。
    
      int status;
     needDetachEnv = false;
     status = g_jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
     if(status < 0){
         if (g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
             JNI_LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
             return;
         }
         needDetachEnv = true;
     }
    

1.3 实现步骤

  1. Java中声明Native方法(即需要调用的本地方法)
  2. 编译上述 Java源文件javac(得到 .class文件)
  3. 通过 javah 命令导出JNI的头文件(.h文件)
  4. 使用 Java需要交互的本地代码 实现在 Java中声明的Native方法

Java 需要与 C 交互,那么就用C 实现 JavaNative方法

  1. 编译.so库文件
  2. 通过Java命令执行 Java程序,最终实现Java调用本地代码

更加详细过程请参考本文第4节:具体使用

NDK全称:Native Development Kit。

NDK是一系列工具的集合。

NDK是属于 Android 的,与Java并无直接关系

NDK提供了一系列的工具,帮助开发者快速开发C(或C )的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。

NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。

NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

其实:NDK就是能够方便快捷开发.so文件的工具。JNI的过程比较复杂,生成.so需要大量操作,而NDK就是简化了这个过程。

打个不恰当比方:JNI 需要自己买菜、洗菜、炒菜、煮饭,很麻烦;NDK就是宅急送,一个电话。

JNI和NDK关系

美高梅网投网址 6

JNI和NDK关系

Application.mk

Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。

Application.mk更多信息可以看这里

附上我的Application.mk

APP_PLATFORM := android-9  
APP_STL := stlport_static #NDK构建系统提供由Android系统给出的最小C  运行时库的C  头文件。
APP_CPPFLAGS := -frtti -fexceptions  #允许异常功能,及运行时类型识别 
APP_CPPFLAGS  = -std=c  11 #允许使用c  11的函数等功能 
APP_CPPFLAGS  = -fpermissive #此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过
#APP_ABI := all
APP_ABI := armeabi-v7a //生成ABI对应的so

OK,开始讲解代码了

1.2 为什么要有 JNI

  • 背景:实际使用中,Java 需要与 本地代码 进行交互
  • 问题:因为 Java 具备跨平台的特点,所以Java 与 本地代码交互的能力非常弱
  • 解决方案: 采用 JNI特性 增强 Java 与 本地代码交互的能力

NDK开发常见步骤

1.配置 Android NDK环境

2.创建 Android 项目,并与 NDK进行关联

3.在 Android 项目中声明所需要调用的 Native方法

4.使用 Android需要交互的本地代码 实现在Android中声明的Native方法 ,比如 Android 需要与 C 交互,那么就用C 实现 Java的Native方法

5.通过 ndk - bulid 命令编译产生.so库文件

6.编译 Android Studio 工程,从而实现 Android 调用本地代码

开发流程

本文根据版本的不同介绍了两种在Android Studio中实现NDK的方法:Android Studio 2.2 以下和2.2以上。

编译问题

  1. Android NDK 'std::string' has not been declared

     需要让Android NDK支持STL
     将Application.mk放在jni目录下(内容如下)
     APP_STL := stlport_static
    
     头文件中#include <string>就OK了, 
     注意使用std::string或加上using namespace std;
     #include <string>
     using namespace std;
     class X {
         public:
         void a(string);
         void b(std::string);
     };
    
  2. Android2.2:'pthread_rwlock_t' does not name a type: android 2.3版本以下不支持读写锁的解决办法

     在Android API < 9时,
     采用android NDK编译代码是不支持pthread_rwlock_t结构体的。
     当时给的解决办法是改写application.mk文件,
     把版本改成9,APP_PLATFORM := android-9//对应2.3.1
    
  3. 不支持C 11函数

     在Application中添加APP_CPPFLAGS  =-std=c  11 未生效
     最后修改使用到C  11的代码改为低版本的书写方式
    

1.1 简介

  • 定义:Java Native Interface,即 Java本地接口
  • 作用: 使得Java 与 本地其他类型语言(如C、C )交互

即在 Java代码 里调用 C、C 等语言的代码 或 C、C 代码调用 Java 代码

  • 特别注意:
    1. JNIJava 调用 Native 语言的一种特性
    2. JNI 是属于 Java 的,与 Android 无直接关系

参考文献

  • 向您的项目添加 C 和 C 代码(Google官方文档)
  • Android:JNI 与 NDK到底是什么?(含实例教学)
  • Android NDK开发扫盲及最新CMake的编译使用
  • Android Studio 手把手教你NDK打包SO库文件,并提供对应API 使用它(赋demo)
  • Android 开发艺术探索——JNI和NDK编程
  • Android Studio NDK及so文件开发(一)
  • NDK开发 从入门到放弃(七:Android Studio 2.2 CMAKE 高效NDK开发)

JNI 相应的格式

JNI参数类型

JNI方法参数

I

欢迎关注Carson_Ho的简书!

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度

美高梅网投网址 7

JNI

JNI(Java Native Interface),Java本地接口,是为方便java调用C或者C 等本地代码所封装的一层接口。由于Java的跨平台性导致本地交互能力不好,一些和操作系统相关的特性Java无法完成,于是Java提供了JNI专门用于和本地代码交互。
无论是 Linux,Windows 还是 Mac OS,或者一些汇编语言写的底层硬件驱动都是 C/C 写的。Java和C/C 不同 ,它不会直接编译成平台机器码,而是编译成虚拟机可以运行的Java字节码的.class文件,通过JIT技术即时编译成本地机器码,所以有效率就比不上C/C 代码,JNI技术就解决了这一痛点,JNI 可以说是 C 语言和 Java 语言交流的适配器、中间件。其实主要是定义了一些JNI函数,让开发者可以通过调用这些函数实现Java代码调用C/C 的代码,C/C 的代码也可以调用Java的代码,这样就可以发挥各个语言的特点了。

碰到的问题

步骤2: 关联Andorid Studio项目 与 NDK

  • 当你的项目每次需要使用 NDK 时,都需要将该项目关联到 NDK
  1. 此处使用的是Andorid Studio,与Eclipse不同
  2. 还在使用Eclipse的同学请自行查找资料配置
  • 具体配置如下

a. 在Gradlelocal.properties中添加配置

ndk.dir=/Users/Carson_Ho/Library/Android/sdk/ndk-bundle

ndk目录存放在SDK的目录中,并命名为ndk-bundle,则该配置自动添加

美高梅网投网址 8示意图

b. 在Gradlegradle.properties中添加配置

android.useDeprecatedNdk=true // 对旧版本的NDK支持

美高梅网投网址 9示意图

c. 在Gradle的build.gradle添加ndk节点

美高梅网投网址 10示意图

  • 至此,将Andorid Studio的项目 与 NDK 关联完毕
  • 下面,将真正开始讲解如何在项目中使用NDK

Android Studio 2.2 以下版本

步骤如下

  1. 配置Android NDK环境
  2. 关联Andorid Studio项目 与NDK
  3. 创建本地代码文件(即需要在 Android项目中调用的本地代码文件)
  4. 创建Android.mk文件 & Application.mk文件
  5. 通过ndk-build命令编译上述文件,生成.so库文件,并放入到工程文件中
  6. 在Andoird Studio项目中使用NDK实现JNI功能

由于我学习的时候所使用的Android Studio版本已经是3.0了,所以这种方法已经失效了。编译时报错如下:

Error:Execution failed for task ':app:compileDebugNdk'.

> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.

  Consider using CMake or ndk-build integration. For more information, go to:

[https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile](https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile)

   To get started, you can use the sample ndk-build script the Android

   plugin generated for you at:

   C:UserspeterMyAppJNIDemoappbuildintermediatesndkdebugAndroid.mk

  Alternatively, you can use the experimental plugin:

[https://developer.android.com/r/tools/experimental-plugin.html](https://developer.android.com/r/tools/experimental-plugin.html)

  To continue using the deprecated NDK compile for another 60 days, set

  android.deprecatedNdkCompileLease=1519725194213 in gradle.properties

相关链接

Android官网NDK入门指南(中文版)

JNI/NDK开发指南

AndroidJNI 通过C 调用JAVA

JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

Android NDK之----- C调用Java [GetMethodID方法的使用]

结果展示

美高梅网投网址 11结果展示

小结

从一个小白的视角记录一下JNI编程需要掌握的基本概念以及自己跑个Hello world,做个学习笔记,感谢链接中作者的无私奉献。如果需要近一步学习可以参看JNI学习总结(实践篇)。

Android.mk

Android.mk文件:是Android提供的一种makefile文件,用来指定诸如编译生成so库名,编译的.cc/.cpp文件和.a静态文件。

以下是我这边用到的Android.mk。有使用到.a静态依赖,循环文件夹便利.cc/.cpp文件

关于Android.mk文件的详解,可以点击这里

LOCAL_PATH := $(call my-dir)

#依赖sodium.a 静态库
include $(CLEAR_VARS)
LOCAL_MODULE := sodium
LOCAL_SRC_FILES := ../ThirdParty/sodium/libsodium.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
#编译模块时要使用的附加的连接器选项,关联log
LOCAL_LDLIBS := -llog
LOCAL_CFLAGS := -DDEBUG

NDK_DEBUG=1
#生成的so库名称
LOCAL_MODULE := network_hermes 

#依赖静态包
LOCAL_C_INCLUDES :=  $(LOCAL_PATH)/../ThirdParty/sodium
LOCAL_STATIC_LIBRARIES  = sodium

#下方循环遍历.cc/.cpp文件进行编译
MY_FILES_PATH  :=  $(LOCAL_PATH)/../Core $(LOCAL_PATH)
MY_FILES_PATH   =  $(LOCAL_PATH)/../ThirdParty/jsoncpp
MY_FILES_PATH   =  $(LOCAL_PATH)/../ThirdParty/sodium
MY_FILES_PATH   =  $(LOCAL_PATH)/../ThirdParty/crypt/src

MY_FILES_SUFFIX := %.cpp
MY_FILES_SUFFIX  = %.cc

rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) )
MY_ALL_FILES := $(MY_ALL_FILES:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES))
MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)

define uniq =
  $(eval seen :=)
  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen  = $_)))
  ${seen}
endef

MY_ALL_DIRS := $(dir $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*/) ) )
MY_ALL_DIRS := $(call uniq,$(MY_ALL_DIRS))


#表示需要编译的文件
LOCAL_SRC_FILES  := $(MY_SRC_LIST)
#表示头文件的搜索路径
LOCAL_C_INCLUDES  = $(MY_ALL_DIRS)

include $(BUILD_SHARED_LIBRARY)

美高梅网投网址 12

NDK

NDK(Native Development Kit),是android提供的一个工具合集,帮助开发者快速开发C(或C )的动态库,并能自动将.so和java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或LINUX系统环境),并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出.so。

美高梅网投网址 13

NDK特点

JNI开发

4.1 Android Studio2.2 以下实现NDK

  • 步骤如下

    1. 配置 Android NDK环境
    2. 关联 Andorid Studio项目 与 NDK
    3. 创建本地代码文件(即需要在 Android项目中调用的本地代码文件)
    4. 创建 Android.mk文件 & Application.mk文件
    5. 编译上述文件,生成.so库文件,并放入到工程文件中
    6. Andoird Studio项目中使用 NDK实现 JNI 功能
  • 步骤详解

ABI

ABI(Application binary interface)应用程序二进制接口。不同的CPU 与指令集的每种组合都有定义的 ABI (应用程序二进制接口),一段程序只有遵循这个接口规范才能在该 CPU 上运行,所以同样的程序代码为了兼容多个不同的CPU,需要为不同的 ABI 构建不同的库文件。当然对于CPU来说,不同的架构并不意味着一定互不兼容。

armeabi设备只兼容armeabi;
armeabi-v7a设备兼容armeabi-v7a、armeabi;
arm64-v8a设备兼容arm64-v8a、armeabi-v7a、armeabi;
X86设备兼容X86、armeabi;
X86_64设备兼容X86_64、X86、armeabi;
mips64设备兼容mips64、mips;
mips只兼容mips;

就目前市场份额而言,绝大部分的设备都已经是armeabi-v7a、arm64-v8a,可以考虑只保留armeabi-v7a架构的SO文件,这样能获得更好的性能效果。

最后

  1. cd到jni目录,输入ndk-build进行编译,最后会在jni同级目录生成libs文件,.so文件就在里面

  2. 回到AS编辑器中,将.so放置在jniLibs文件夹下(如没有,则创建一个,与java文件夹同级)

  3. 在build.gradle添加JNI配置

     buildTypes {
         sourceSets.main {
             jni.srcDirs = []//disable automatic ndk-build call
         }
     } 
    

4.运行工程,检查是否成功

步骤4:创建Android.mk文件

  • 作用:指定源码编译的配置信息

如工作目录,编译模块的名称,参与编译的文件等

  • 具体使用

Android.mk

LOCAL_PATH := $(call my-dir)// 设置工作目录,而my-dir则会返回Android.mk文件所在的目录include $(CLEAR_VARS)// 清除几乎所有以LOCAL——PATH开头的变量(不包括LOCAL_PATH)LOCAL_MODULE := hello_jni// 设置模块的名称,即编译出来.so文件名// 注,要和上述步骤中build.gradle中NDK节点设置的名字相同LOCAL_SRC_FILES := test.cpp// 指定参与模块编译的C/C  源文件名include $(BUILD_SHARED_LIBRARY)// 指定生成的静态库或者共享库在运行时依赖的共享库模块列表。// 示例说明// 示例1:c  文件参与编译 // 单个c  文件参与编译 include $(CLEAR_VARS) LOCAL_MODULE := Carsontest // 编译出来的文件名 LOCAL_SRC_FILES := test1.cpp // 多个c  文件参与编译 include $(CLEAR_VARS) LOCAL_MODULE := Carsontest // 编译出来的文件名 LOCAL_SRC_FILES :=  test1.cpp  test/ftest2.cpp  test3.c // 示例2:.so文件参与模块编译 include $(CLEAR_VARS) LOCAL_MODULE := Carsontest // 编译出来的文件名 LOCAL_SRC_FILES := test/Carsontest.so include $(PREBUILT_SHARED_LIBRARY) // 设置可被依赖// 示例3:设置需依赖的.so文件 LOCAL_SHARED_LIBRARIES :=  test1  Carsontest

最后,将上述文件同样放在src/main/jni文件夹中。

基本概念

使用JNI的函数以及复杂对象传递

可以查看JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

源码地址

Carson-Ho的Github地址:NDK_Demo

JNI的头文件

1.首先在AS中创建一个类,不以自己的项目为例了,写了个HelloJNI来看:

public class JniTest {
static{
    System.loadLibrary("hello-jni");
}

public static final native String helloJni();
}

然后通过命令javah -jni XX.XX.XX.JNITest就可以在java文件夹下生成头文件。格式如下:

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

#ifndef _Included_mogujie_com_jnitestapp_JniTest
#define _Included_mogujie_com_jnitestapp_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     mogujie_com_jnitestapp_JniTest
 * Method:    helloJni
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_mogujie_com_jnitestapp_JniTest_helloJni
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

步骤2:根据需求使用NDK

  • 配置好NDK后,Android Studio会自动生成C 文件并设置好调用的代码
  • 你只需要根据需求修改C 文件 & Android就可以使用了。

美高梅网投网址 14示意图

  • 本文主要讲解 JavaJNIAndroidNDK相关知识
  • 下面我将继续对 Android中的NDK进行深入讲解 ,有兴趣可以继续关注Carson_Ho的安卓开发笔记

相关文章阅读Android开发:最全面、最易懂的Android屏幕适配解决方案

Android事件分发机制详解:史上最全面、最易懂Android开发:史上最全的Android消息推送解决方案Android开发:最全面、最易懂的Webview详解Android开发:JSON简介及最全面解析方法!Android四大组件:Service服务史上最全面解析Android四大组件:BroadcastReceiver史上最全面解析

开始编写JNI

在工程根目录下新建jni目录,<mark>一定要创建jni目录,否者ndk-build不知道执行文件</mark>,在这个文件夹下面创建Android.mk和Application.mk两个文件,附上我的jni目录文件结构图和make文件

文件结构图

.
├── Android.mk
├── Application.mk
├── network_jni.cpp
└── network_jni.h

2.2 使用步骤

  1. 配置 Android NDK环境
  2. 创建 Android 项目,并与 NDK进行关联
  3. Android 项目中声明所需要调用的 Native方法
  4. 使用 Android需要交互的本地代码 实现在Android中声明的Native方法

比如 Android 需要与 C 交互,那么就用C 实现 JavaNative方法

  1. 通过 ndk - bulid 命令编译产生.so库文件
  2. 编译 Android Studio 工程,从而实现 Android 调用本地代码

更加详细过程请参考本文第4节:具体使用

美高梅网投网址 15示意图

本文根据版本的不同介绍了两种在Android Studio中实现 NDK的方法:Android Studio2.2 以下 & 2.2以上

前期准备- NDK环境搭建

1.下载NDK的包(Mac OS X版本)
目前最新版本android-ndk-r13b-darwin-x86_64.zip,下载链接点击此处,

2.配置环境变量

1. 启动终端Terminal

2. 进入当前用户的home目录
    输入cd ~

3. 创建.bash_profile
    输入touch .bash_profile

4. 编辑.bash_profile文件
    输入open -e .bash_profile

.bash_profile 文件内容:
    export PAHT=$PATH:/Users/work/dev_app/adt-bundle-mac/sdk/tools
    export PATH=$PATH:/Users/work/dev_app/android-ndk-r8c/
    ANDROID_NDK_ROOT=/Users/work/dev_app/android-ndk-r8c/
    export ANDROID_NDK_ROOT 
    ANDROID_SDK_ROOT=/Users/work/dev_app/adt-bundle-mac/sdk/
    exprot ANDROID_SDK_ROOT

5. 保存文件,关闭.bash_profile

6. 更新刚配置的环境变量
    输入source .bash_profile

7.输入ndk-build试试
    出现这个结果就是正确
    Android NDK: Could not find application project directory !
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.
    /Users/bujue/mogujie/soft/ndk/android-ndk-r10e/build/core/build-    local.mk:143: *** Android NDK: Aborting    .  Stop.

步骤7:在src/main/中创建一个名为jniLibs的文件夹,并将上述生成的so文件夹放到该目录下

  1. 要把名为 CPU平台的文件夹放进去,而不是把.so文件放进去
  2. 如果本来就有.so文件,那么就直接创建名为jniLibs的文件夹并放进去就可以

美高梅网投网址 16示意图

JNI的C文件

复制刚才生成的头文件,然后将后缀名改为.cpp.并实现hellojni的方法,格式如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "hellojni.h"
#include <android/log.h>
/* Header for class mogujie_com_jnitestapp_MainActivity */
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     mogujie_com_jnitestapp_MainActivity
 * Method:    helloJni
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_mogujie_com_jnitestapp_JniTest_helloJni(JNIEnv *env, jclass jobj){

    // return (*env)->NewStringUTF(env,"czz重温android studio生产so文件");

    jstring str = env->NewStringUTF("123123123123");
    return str;
}

#ifdef __cplusplus
}
#endif

步骤3:创建本地代码文件

  • 即需要在Android项目中调用的本地代码文件

此处采用 C 作为展示

test.cpp

# include <jni.h># include <stdio.h>extern "C"{ JNIEXPORT jstring JNICALL Java_scut_carson_1ho_ndk_1demo_MainActivity_getFromJNI(JNIEnv *env, jobject obj ){ // 参数说明 // 1. JNIEnv:代表了VM里面的环境,本地的代码可以通过该参数与Java代码进行操作 // 2. obj:定义JNI方法的类的一个本地引用 return env -> NewStringUTF("Hello i am from JNI!"); // 上述代码是返回一个String类型的"Hello i am from JNI!"字符串 }}

此处需要注意:

  • 如果本地代码是C .cpp或者.cc),要使用extern "C" { }把本地方法括进去
  • JNIEXPORT jstring JNICALL中的JNIEXPORTJNICALL不能省
  • 关于方法名Java_scut_carson_1ho_ndk_1demo_MainActivity_getFromJNI

    1. 格式 = Java _包名 _ 类名_Java需要调用的方法名
    2. Java必须大写
    3. 对于包名,包名里的.要改成__要改成_1

    如我的包名是:scut.carson_ho.ndk_demo,则需要改成scut_carson_1ho_ndk_1demo

最后,将创建好的test.cpp文件放入到工程文件目录中的src/main/jni文件夹

若无jni文件夹,则手动创建。

下面我讲解一下JNI类型与Java类型对应的关系介绍

美高梅网投网址 17如下图

步骤1:配置 Android NDK环境

具体请看文章一定能成功的Android NDK环境配置教程

步骤8:在Andoird Studio项目中使用NDK实现JNI功能

  • 此时,我们已经将本地代码文件编译成.so库文件并放入到工程文件中
  • Java代码中调用本地代码中的方法,具体代码如下:

MainActivity.java

public class MainActivity extends AppCompatActivity { // 步骤1:加载生成的so库文件 // 注意要跟.so库文件名相同 static { System.loadLibrary("hello_jni"); } // 步骤2:定义在JNI中实现的方法 public native String getFromJNI(); // 此处设置了一个按钮用于触发JNI方法 private Button Button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 通过Button调用JNI中的方法 Button =  findViewById(R.id.button); Button.setOnClickListener(new View.OnClickListener() { @Override public void onClick { Button.setText(getFromJNI; }

主布局文件:activity_main.xml

<RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="scut.carson_ho.ndk_demo.MainActivity"> // 此处设置了一个按钮用于触发JNI方法 <Button android: android:layout_centerInParent="true" android:layout_width="300dp" android:layout_height="50dp" android:text="调用JNI代码" /></RelativeLayout>

步骤6:编译上述文件,生成.so库文件

  • 经过上述步骤,在src/main/jni文件夹中已经有3个文件

美高梅网投网址 18示意图

  • 打开终端,输入以下命令
// 步骤1:进入该文件夹cd /Users/Carson_Ho/AndroidStudioProjects/NDK_Demo/app/src/main/jni // 步骤2:运行NDK编译命令ndk-build

美高梅网投网址 19示意图

  • 编译成功后,在src/main/会多了两个文件夹libs & obj,其中libs下存放的是.so库文件

美高梅网投网址 20示意图

美高梅网投网址 21目录

步骤1:按提示创建工程

在创建工程时,需要配置 NDK,根据提示一步步安装即可。

美高梅网投网址 22示意图

4.2 Android Studio2.2 以上实现NDK

  • 如果你的Android Studio是2.2以上的,那么请采用下述方法

因为Android Studio2.2以上已经内部集成 NDK,所以只需要在Android Studio内部进行配置就可以

  • 步骤讲解
  • Android开发中,使用 NDK开发的需求正逐渐增大
  • 但很多人却搞不懂 JNINDK 到底是怎么回事
  • 今天,我将先介绍JNINDK & 之间的区别,手把手进行 NDK的使用教学,希望你们会喜欢

步骤5:创建Application.mk文件

  • 作用:配置编译平台相关内容
  • 具体使用

Application.mk

APP_ABI := armeabi// 最常用的APP_ABI字段:指定需要基于哪些CPU平台的.so文件// 常见的平台有armeabi x86 mips,其中移动设备主要是armeabi平台// 默认情况下,Android平台会生成所有平台的.so文件,即同APP_ABI := armeabi x86 mips// 指定CPU平台类型后,就只会生成该平台的.so文件,即上述语句只会生成armeabi平台的.so文件

最后,将上述文件同样放在src/main/jni文件夹中

TAG标签:
版权声明:本文由美高梅网投平台发布于美高梅网投网址,转载请注明出处:Android从头再来,JNI学习总结