android studio + NDK + JNI

推荐:

android studio 中 添加.so 文件

https://blog.youkuaiyun.com/carson_ho/article/details/73250163

jni (java)

定义:Java Native Interface,即 Java本地接口

作用: 使得Java 与 本地其他类型语言(如C、C++)交互

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

特别注意:

JNI是 Java 调用 Native 语言的一种特性
JNI 是属于 Java 的,与 Android 无直接关系

ndk (android)

定义:Native Development Kit,是 Android的一个工具开发包
NDK是属于 Android 的,与Java并无直接关系

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

即可通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互

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

即 Android开发的功能需要本地代码(C/C++)实现

JNI介绍:

Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。

首先看一下Android平台的框架图:

可以看到Android上层的Application和ApplicationFramework都是使用Java编写,
底层包括系统和使用众多的LIiraries都是C/C++编写的。
  所以上层Java要调用底层的C/C++函数库必须通过Java的JNI来实现。

android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和C/C++代码。其中C/C++代码用于生成库文件,Java代码用于引用C /C++库文件以及调用C/C++方法。

NDK介绍

Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”。

NDK提供了一份稳定、功能有限的API头文件声明
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。

NDK产生的背景:
  Android平台从诞生起,就已经支持C、C++开发。众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言。但这并不等同于“第三方应用只能使用Java”。在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,“Java+C”的编程方式是一直都可以实现的。
  不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,Android SDK文档里,找不到任何JNI方面的帮助。即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?这里面也存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。开发者需要自行斟酌使用。
  于是NDK就应运而生了。NDK全称是Native Development Kit。
  NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。NDK将是Android平台支持C开发的开端。
  
概念:
(1).NDK是一系列工具的集合。帮助开发者快速开发C/C++的动态库。
并能自动将so和Java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
(2).NDK将是Android平台开发支持C开发的开端。

作用:
(1).代码的保护。由于Java层代码很容易被反编译,而C/C++库反编译难度很大。
(2).可以方便的使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
(3).提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
(4).便于移植。用C/C++写的库可以方便的在其他的嵌入式平台上再次使用。

jni头文件:

JNIEXPORT void JNICALL Java_com_test01_Test_firstTest   
(JNIEnv * env, jobject obj,
	jshort, jint, jlong, jfloat, jdouble, jchar, 
	jboolean, jbyte, jstring, jobject, jobject, jintArray);  

JNIEXPORT void :指jni输出的返回值

JNICALL Java_com_test01_Test_firstTest :指jni调用java的方法:Java_包名_方法名

JNIEnv * env :任意native函数的第一个参数,用于访问JVM数据结构的JNI函数表指针

jobject obj 或 jclass cls :任意native函数的第二个参数,调用java中native方法的实例或Class对象,如果这个native方法是实例方法,则该参数是jobject,如果是静态方法,则是jclass

除了 JNIEnv 和 jclass 这两个参数外,其它参数都是一一对应的。下面是JNI 规范文档中描述 Java 与 JN I数据类型的对应关系:

基本类型:
在这里插入图片描述
引用类型:
在这里插入图片描述

JNI/NDK 开发指南

Android NDK编程中Application.mk和Android.mk写法

Android.mk:

#必须位于android.mk文件的开始,定位源文件的位置,$(call my-dir)返回当前路径,即android.mk所在的目录
LOCAL_PATH := $(call my-dir)

# 编译静态/动态库的开始,用于去除这之前的编译环境,必须要
include $(CLEAR_VARS)

# 编译出的静态/动态库的名字,java代码System.loadLibrary("hello");加载的就是它
LOCAL_MODULE := hello

# 需要编译为静态/动态库的c或c++源文件,带后缀
LOCAL_SRC_FILES := hello.cpp

# 把当前模块编译为共享库,前缀为lib,后缀为.so
include $(BUILD_SHARED_LIBRARY)
# 把当前模块编译为静态库,前缀为lib,后缀为.a	
include $(BUILD_STATIC_LIBRARY)

Application.mk

# 支持的ABI平台,多个用空格隔开
# APP_ABI := all
APP_ABI :=armeabi armeabi-v7a

# Android 平台的名称
APP_PLATFORM := android-14

# 选择 GCC 编译器的版本。 其中,64 位 ABI 默认使用版本 4.9 ,32 位 ABI 默认使用版本 4.8。
NDK_TOOLCHAIN_VERSION := 4.9

APP_ABI

默认情况下,NDK 构建系统为 armeabi ABI 生成机器代码。这个变量是为了生成不同架构下使用的so包,此机器代码对应于基于 ARMv5TE、采用软件浮点运算的 CPU, 可以使选用以下不同的 ABI:

armeabi-v7a:基于 ARMv7 的设备上的硬件 FPU 指令 
arm64-v8a:ARMv8 AArch64 
x86:IA-32 
x86_64:Intel64 
mips:MIPS32 
mips64:MIPS64 (r6) 
all:所有支持的指令集

一、新建项目,然后新建module并关联module。

Markdown

二、下载NDK

Android studio此处有NDK的下载路径,直接下载即可。下载后效果如图。我这里下载过,所以直接显示了NDK的路径。
Markdown

Markdown

三、配置:

1、gradle.properties中末尾添加:

android.useDeprecatedNdk=true

如图:

Markdown

2、新建module的build.gradle中的

①、defaultConfig中的末尾添加:

 ndk {
            moduleName "JniTest"
            ldLibs "log", "z", "m"
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }

注意:moduleName用于后面的:

static {
        System.loadLibrary("JniTest");
        //加载实现了native函数的动态库,只需要写动态库的名字
    }

②、buildTypes中的末尾添加:

 sourceSets {
            main {
                jni.srcDir '/src/main/jni'
            }
        }

注意:这里jni.srcDir是jni文件夹位置

效果如图:

Markdown

四、定义native方法并编译

1、在新建的module中新建NdkString类,加载动态库,并定义native方法。

Markdown

2、build - make module : 编译Java文件成class,找到生成的class文件如图即为成功。

Markdown

五、生成头文件

打开文件夹看到如图:

Markdown

然后cmd打开命令行,执行

C:\Users\Administrator>e:

E:\>cd E:\android\MyApplication4\mylibrary\src\main

E:\android\MyApplication4\mylibrary\src\main>javah -d jni  -classpath ..\..\build
\intermediates\classes\debug com.example.mylibrary.NdkString

E:\android\MyApplication4\mylibrary\src\main>

第一、二行,进入module的src/main目录
第三行,javah命令生成头文件,
第四行,表示成功生成,

成功生成后src/main目录下会生成Jni文件夹和头文件。如下图:

Markdown

六、写c的逻辑

Markdown

1、新建c文件:hello.c,引入头文件并将头文件的

JNIEXPORT jstring JNICALL Java_com_example_mylibrary_NdkString_getFromC
  (JNIEnv *, jclass);

复制到hello.c

2、实现自己的一些逻辑,这里直接返回一个字符串

#include "com_example_mylibrary_NdkString.h"

JNIEXPORT jstring JNICALL Java_com_example_mylibrary_NdkString_getFromC
        (JNIEnv * env, jclass jclass){

        //return (*env)->NewStringUTF(env,"From C");
        return (*env)->NewStringUTF(env,"Hello From JNI!");
}

3、jni目录下添加一个空的c文件。

效果如图:

Markdown

注意:jni文件夹必须新建一个空的c文件,此为windows的bug。

七、生成so文件

rebuild project,然后根据目录找到so,即表示生成生成。

Markdown

八、调用c文件:

布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.myapplication.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onclick"
        android:text="我是原生的Android字符串" />

</android.support.constraint.ConstraintLayout>

Java文件:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onclick(View view) {
        ((Button) view).setText(NdkString.getFromC());
    }
}

运行效果如图:

Markdown

参考:

慕课网

Android开发之Android Studio NDK开发步骤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值