Android开发之NDK/JNI的hello world

前言

由于公司最近业务需要,找第三方开发了C++库。而我作为对接人需要提前了解如何去对接C++的接口,于是便有了这篇文章。
说实话,我不太会写概念性的描述,所以便从网上找了一些相关介绍粘贴出来(罪过)。

● NDK
Native Development Kit(NDK)是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C/C++的动态库,并能自动将so和Java一起打包成apk。

● JNI
Java Native Interface(JNI)标准是java平台的一部分,JNI是Java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用C/C++代码,C/C++的代码也可以调用java代码。

● JNI与NDK的关系
NDK可以为我们生成了C/C++的动态链接库,JNI是java和C/C++沟通的接口,两者与Android没有半毛钱关系,只因为安卓是java程序语言开发,然后通过JNI又能与C/C++沟通,所以我们可以使用NDK+JNI来实现“Java+C”的开发方式。

● 为什么要NDK开发
NDK开发具有以下优点: 
    1. 项目需要调用底层的一些C/C++的一些东西(java无法直接访问到操作系统底层(如系统硬件等)),或者已经在C/C++环境下实现了功能代码(大部分现存的开源库都是用C/C++代码编写的。),直接使用即可。NDK开发常用于驱动开发、无线热点共享、数学运算、实时渲染的游戏、音视频处理、文件压缩、人脸识别、图片处理等。 
    2. 为了效率更加高效些。将要求高性能的应用逻辑使用C/C++开发,从而提高应用程序的执行效率。但是C/C++代码虽然是高效的,在java与C/C++相互调用时却增大了开销; 
    3. 基于安全性的考虑。防止代码被反编译,为了安全起见,使用C/C++语言来编写重要的部分以增大系统的安全性,最后生成so库(用过第三方库的应该都不陌生)便于给人提供方便。(任何有效的代码混淆对于会smail语法反编译你apk是分分钟的事,即使你加壳也不能幸免高手的攻击) 
    4. 便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

正文

接下来就直接开始demo的过程吧!
工欲善其事必先利其器,在开发开始的第一步当然是搭建好开发环境,由于现在android开发大部分使用的是android studio(AS),在AS上配置NDK的开发环境是非常简洁的

环境的搭建 ##

  1. step1
    下载NDK开发相关的编译器和组件
    这里写图片描述
  2. step2
    选用NDK
    这里写图片描述
    选择好后,AS会在local.properties文件中加入
ndk.dir=/Users/temp/Library/Android/sdk/ndk-bundle  //增加
sdk.dir=/Users/temp/Library/Android/sdk

环境基本上是搭建好了,就可以开始开发了。

开发

先写一个java文件

package zhaozx;

/**
 * Created by zhaozx on 2017/6/6.
 * desc:
 */

public class JniTest {
    /**
     * 将用C++代码实现,在android代码中调用的方法:获取当前app的包名
     * @return
     */
    public static native String getPackname();

    /**
     * 加载so库或jni库,在使用到该库之前加载就行,不一定非要写在这个类内
     * 系统自己会判断扩展名是dll还是so,这里加载libapp.so
     */
    static {
        System.loadLibrary("app");
    }
}

接下来是比较重要的步骤,使用javah根据java文件生成c/c++的.h头文件
1.进入app/src/main目录
2.使用 javah -d jni -classpath ./Java 包名+类名(我的是zhaozx.JniTest),生成zhaozx_JniTest.h文件, 此文件在main/jni的目录下, -d jni 就是在当前目录(main)下创建jni文件夹。
我的zhaozx_JniTest.h文件的内容为

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

#ifndef _Included_zhaozx_JniTest
#define _Included_zhaozx_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     zhaozx_JniTest
 * Method:    getPackname(方法名)
 * Signature: (Ljava/lang/Object;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_zhaozx_JniTest_getPackname
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

参照zhaozx_JniTest.h文件在jni目录下创建对应的zhaozx_JniTest.cpp文件,我的文件内容为

//
// Created by Apple on 2017/6/6.
//
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
#include "zhaozx_JniTest.h"

JNIEXPORT jstring JNICALL Java_zhaozx_JniTest_getPackname (JNIEnv *env, jclass cls){
    return (env)->NewStringUTF((char *)"Hello from JNI !");
    /**
    此处根据文件后缀写法不同
    .cpp的写法return (env)->NewStringUTF((char *)"Hello from JNI !");
    .c的写法return (*env)->NewStringUTF(env, "Hello from JNI !");
    */
}

在app/build.gradle文件中加入

defaultConfig {
        applicationId "com.learnrn"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
        // 生成不同平台的.so文件
        +ndk {
        +    abiFilters "armeabi-v7a", "x86"
        +}
    }

在jni目录中还需要加入两个文件Android.mk和Application.mk。
Android.mk的内容

LOCAL_PATH := $(call my-dir)     // 设置当前的编译目录(Android.mk所在的目录)

include $(CLEAR_VARS)            // 清除LOCAL_XX变量(LOCAL_PATH除外)
LOCAL_MODULE := TEST_JNI  // 指定当前编译模块的名称(不过貌似没用额,总是生成libapp.so文件)
LOCAL_SRC_FILES := jnitest.cpp    // 编译模块需要的源文件
include $(BUILD_SHARED_LIBRARY) // 指定编译出的库类型,BUILD_SHARED_LIBRARY:动态库;//BUILD_STATIC_LIBRARY:静态库, BUILD_EXECUTEABLE指:可执行文件

Application.mk的内容

APP_ABI := all

做完这些后就大工告成,在AS中rebuild项目。
这里写图片描述

会发现这个目录下会多出.so文件。

使用

.so文件已经生成,接下来就是如何使用了,在需要调用的地方创建对象并使用方法即可,如:

JniTest j = new JniTest();
Log.e("我要成功", j.getPackname());

结果
这里写图片描述

总结

整个过程结束了,但是还是有很多需要注意的地方
1.生成.h文件的过程(主要是路径要对)
2.根据.h文件编写相对应的C/C++文件
3.C/C++文件里面相对应实现java方法的写法是有差异的
4.static {
    System.loadLibrary("XXX");
}"XXX"的名字必须是生成的libXXX.so,但当XXX自己以lib打头时也可以。

目前想到的就这么多了,谢谢大家哈!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值