Android NDK基础

一、JNI 的概念

1、概念

JNI全称 Java Native Interface,Java 本地接口。可以通过JNI调用系统提供的API。

2、JNI 与 NDK 的区别
  • JNI:JNI是一套编程接口,用来实现Java代码与本地C/C++代码的交互。
  • NDK:NDK是Google开发的一套开发和编译工具集,可以生成动态链接库,主要用于Android的JNI开发。
3、JNI 的作用
  • 扩展: JNI扩展了JVM能力,驱动开发,例如开发一个wifi驱动,可以将手机设置为无限路由。
  • 高效: 本地代码效率高,游戏渲染,音频视频处理等方面使用JNI调用本地代码,C语言可以灵活操作内存。
  • 复用:在文件压缩算法 7zip开源代码库,机器视觉 OpenCV开放算法库等方面可以复用C平台上的代码,不必在开发一套完整的Java体系,避免重复发明轮子。
  • 特殊: 产品的核心技术一般也采用JNI开发,不易破解。

二、JNI 两种注册方式

(一)、静态注册

开发流程如下:

  1. 在 Java 中先声明 native 方法。

  2. 编译 Java 源文件得到 .class 文件。

  3. 使用 Javah -jni 命令生成对应的头文件。

    • 命令 javah -jni packagename.classname 生成由包名加类名命名的 jni 层头文件。
    • 命令 javah -o custom.h packagename.classname,其中 custom.h 为自定义的文件名。
  4. 实现 JNI 里面的函数(生成.c文件),再在Java中通过System.loadLibrary加载 so 库。

PS:javah 是 JDK 自带的一个命令,-jni 参数表示将 class 中用到 native 声明的函数生成 JNI 规则的函数

Demo:

  1. 创建项目

首先创建一个 Android 项目,包名:com.zf.ndkdemo

  1. 创建引用本地库的工具类

创建一个 Java 类 NDKTools

package com.zf.ndkdemo;

public class NDKTools {
    public static native String stringFromJNI();
}

  1. 在MainActivity中调用 NDKTools 的 stringFromJNI()方法
package com.zf.ndkdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.File;

public class MainActivity extends AppCompatActivity {

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

        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(NDKTools.stringFromJNI());
    }
}
  1. 获取classes文件

(1)点击 AndroidStudio 中的 Make Project 或者 Rebuild Project 进行编译获取class文件。(Build --> Make Project 或者 Build --> Rebuild Project)

(2)编译完成之后,按照如下目录寻找class文件。

app/build/intermediates/classes/debug/com/zf/ndkdemo

image

  1. 获取.h文件
    点击AndroidStudio下面Terminal,然后进入app/build/intermediates/classes/debug目录下,然后执行命令:javah -jni com.zf.ndkdemo.NDKTools 。如果一切顺利则会在app/build/intermediates/classes/debug下面生成com_zf_ndkdemo_NDKTools.h文件。如下图:
    image
    内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zf_ndkdemo_NDKTools */

#ifndef _Included_com_zf_ndkdemo_NDKTools
#define _Included_com_zf_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_zf_ndkdemo_NDKTools
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_zf_ndkdemo_NDKTools_stringFromJNI
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
  1. 增加对应的.c文件
    在工程main目录下创建jni文件夹,然后将刚刚生成的.h剪切过来。然后再在jni目录下生成一个.c文件ndkdemo.c
//
// Created by zf on 2019/1/17.
//

#include "com_zf_ndkdemo_NDKTools.h"

JNIEXPORT jstring JNICALL
Java_com_zf_ndkdemo_NDKTools_stringFromJNI
        (JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "Hello world,我的第一行NDK代码");
}
  1. 添加并编写Android.mk文件
    在jni目录下生成一个Android.mk文件,内容如下:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := ndkdemo-jni

LOCAL_SRC_FILES :=ndkdemo.c

include $(BUILD_SHARED_LIBRARY)
  1. 修改相关配置文件
  • local.properties 文件中添加 NDK 路径
ndk.dir=D\:\\Sdk\\ndk-bundle
sdk.dir=D\:\\Sdk
  • 修改 app module 目录下的 build.gradle 文件,内容如下:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.zf.ndkdemo"
        minSdkVersion 18
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        ndk{
            moduleName "ndkdemo-jni"
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'
            }
        }
        sourceSets.main {
            jni.srcDirs = []
            jniLibs.srcDirs = ['src/main/jniLibs']
        }

    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
  • gradle.properties 文件中添加: Android.useDeprecatedNdk=true
  1. 修改引用类 NDKTools
public class NDKTools {
    static {
        System.loadLibrary("ndkdemotest-jni");
    }
    public static native String stringFromJNI();
}

over

(二)、动态注册

开发流程如下:

  1. 编写 Java 的 native 方法;
  2. 编写 JNI 函数的实现(函数名随意);
  3. 利用结构体 JNINativeMethod 保存 Java native 方法和 JNI 函数的对应关系;
  4. 利用 RegisterNatives(JNIEnv*, jclass, const JNINativeMethod*,jint) 注册类的所有本地方法;
  5. 第3、4步的代码在 JNI_OnLoad 方法中执行。
  6. 在编写 Java native 方法的类中通过 System.loadLibrary 加载 JNI 动态库,然后会自动调用 JNI_OnLoad 函数,完成动态注册。

三、JNI 数据类型映射

  • 基本数据类型:
Java类型本地类型(Native Type)描述
booleanjbooleanC/C++无符号8位整型 (unsigned char)
bytejbyteC/C++带符号8位整型 (char)
charjcharC/C+无符号16位整型 (unsigned short)
shortjshortC/C++带符号16位整型 (short)
intjintC/C++带符号32位整型 (int)
longjlongC/C++带符号64位整型 (long)
floatjfloatC/C++32位浮点型 (float)
doublejdoubleC/C++64位浮点型 (double)

基本数据类型的映射即在Java的基本数据类型前面添加 j 就是本地类型的基本数据类型

  • 引用数据类型:
Java类型本地类型(Native Type)描述
Objectjobject任何java对象
ClassjclassClass类对象
Stringjstring字符串对象
Object[]jobjectArray任何对象的数组
boolean[]jbooleanArray布尔型数组
byte[]jbyteArray比特型数组
char[]jcahrArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整型数组
long[]jlongArray长整形数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组
voidvoid

注意:

a、应用数据类型不能在Native层使用,需要根据 JNI 函数转化后才能使用;
b、多维数组都是引用类型。

 //获得一维数组的类引用,即jintArray类型  
jclass intArrayClass = env->FindClass("[I");   
//构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为length,类型为 jsize
jobjectArray obejctIntArray  =  env->NewObjectArray(length ,intArrayClass , NULL); 
  • 方法和变量的id

当Native要调用Java层方法的时候,需要通过JNI函数获取对应的id,根据id调用 JNI 函数获取该方法。变量同理。

id 结构如下:

struct _jfieldID;                       /* opaque structure */
typedef struct _jfieldID* jfieldID;     /* field IDs */

struct _jmethodID;                      /* opaque structure */
typedef struct _jmethodID* jmethodID;   /* method IDs */

时间问题,未完待续…

参考链接:
Android NDK开发:JNI基础篇
Android JNI

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值