Android Studio开发Jni教程-入门篇

    最近研究jni,按照网上的大部分教程操作,发现没有一个写的很详尽的,走了很多弯道,
一番研究之后最后终于调通,于是将详细过程写成了文档,创建jni工程,这一篇就够了

前言

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

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

3.  什么是JNI?
JNI全称为:Java Native Interface。JNI是本地编程接口,它使得在 Java 虚拟机内部运行的 Java代码能够与用其它语言(如 C、C++)编写的代码进行交互。

4.  为什么使用JNI?
JNI的目的是使java方法能够调用c实现的一些函数。

5.  安卓中的so文件是什么?
Android中用到的so文件是一个c++的函数库。在android的JNI中,要先将相应的C语言打包成so库,然后导入到lib文件夹*java调用。

开发环境如下:
操作系统:Windows 7
开发环境:Android Studio 2.3.2

一、ndk安装

从Android Studio安装(下载不下来可能需要*)
1.)打开AndroidStudio,选择顶部工具条,Tools->Android->SDKManager

Android Studio开发Jni教程-入门篇
2.)在弹出来的对话框中选择SDK Tools选项卡

Android Studio开发Jni教程-入门篇


3.)勾选上图中NDK,点击Apply,开始安装
4.)安装完成后,重启Android Studio

5.)下载完的ndk路径一般在Android SDK目录下

Android Studio开发Jni教程-入门篇

6.)配置ndk环境变量

Android Studio开发Jni教程-入门篇

在命令行cmd窗口中直接输入ndk-build出现如下提示即可

 Android Studio开发Jni教程-入门篇

7.)配置ndk的路径

 Android Studio开发Jni教程-入门篇

二、添加ndkExternal Tool

1.      打开setting,选择Tools->External Tools,点击加号

Android Studio开发Jni教程-入门篇

2.添加ndk build

Android Studio开发Jni教程-入门篇

Program的设置可点击右边按钮选择ndk-bundle目录下的ndk-build.cmd

Android Studio开发Jni教程-入门篇

Working directory如下,选择ProjectFileDir后在加上\app\src\main

Android Studio开发Jni教程-入门篇

Program: D:\Android\sdk\ndk-bundle\ndk-build.cmd

Working directory:$ProjectFileDir$\app\src\main

两个参数都设置好之后点击OK

3. 添加javah

Program: $JDKPath$\bin\javah.exe
Parameters: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$
Working directory: $ModuleFileDir$\src\main\Java

Android Studio开发Jni教程-入门篇

三Jni开发

1. 新建工程

Android Studio开发Jni教程-入门篇

2. 在main下新建jni文件夹

Android Studio开发Jni教程-入门篇

勾选change folder location,点击finish

Android Studio开发Jni教程-入门篇

会在app下的build.gradle文件中自动加入下图红框代码

Android Studio开发Jni教程-入门篇

将其修改为

sourceSets.main {
    jniLibs.srcDir 'src/main/libs'
    jni.srcDirs = [] //disable automatic ndk-build call
}
否则最后运行时会出现如下错误

3. 在项目gradle.properties文件中加上以下代码,表示我们要使用NDK进行开发。

android.useDeprecatedNdk=true

Android Studio开发Jni教程-入门篇

更改之后点击sync now

4. 在项目local.properties中加入你自己ndk和sdk的路径:

Android Studio开发Jni教程-入门篇

5. 在app文件夹下的build.gradle中的defaultConfig里加入如下代码

ndk{   

   moduleName"hello"       //生成的so文件名字,调用C程序的代码中会用到该名字   

}

Android Studio开发Jni教程-入门篇

6.在jni文件夹下新建Android.mk

 Android Studio开发Jni教程-入门篇

在Android.mk中写入

LOCAL_PATH :=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=hello   
LOCAL_SRC_FILES :=hello.c
include $(BUILD_SHARED_LIBRARY)

7.新建Application.mk

写入:

APP_MODULES := hello
APP_ABI := armeabi

 Android Studio开发Jni教程-入门篇

8.在MainActivity中加入如下代码
 
static {//固定写法,表示我们要加载的资源文件为libhello.so
    System.loadLibrary("hello");
}
//声明一个本地方法,用native关键字修饰
 public native String getStringFromNative();

布局文件中只有一个textView,设置其id为textView,MainActivity如下:

Android Studio开发Jni教程-入门篇
 
9.编译MainActivity,生成.h文件
在MainActivity出右击,选择External tools ,javah
 

会在jni文件夹下生成“包名+类名”的.h文件

Android Studio开发Jni教程-入门篇

.h文件中的内容是自动生成的

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

#ifndef _Included_cn_cun_jnihello_MainActivity
#define _Included_cn_cun_jnihello_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     cn_cun_jnihello_MainActivity
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_cn_cun_jnihello_MainActivity_getStringFromNative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

 

10.在jni下新建hello.c文件

Android Studio开发Jni教程-入门篇

Android Studio开发Jni教程-入门篇

hello.c中内容为

//引入上面生成的头文件,并实现头文件中声明的方法
#include "cn_cun_jnihello_MainActivity.h"
JNIEXPORT jstring JNICALL Java_cn_cun_jnihello_MainActivity_getStringFromNative(JNIEnv *env, jobject obj){//方法名是Java_包名_类名_方法名
    char *str = "String from native C";
    return (*env)->NewStringUTF(env, str);
}
 

11.编译C文件,生成so文件

在jni文件夹右击选择Externaltools ->ndk build

Android Studio开发Jni教程-入门篇

会生成libs下的so类库

Android Studio开发Jni教程-入门篇

12.至此大功告成,点击运行,把项目跑起来

Android Studio开发Jni教程-入门篇


github项目地址

https://github.com/wutengfei/JniHello


备注:.h文件只是临时文件,用与帮助创建.c文件,.c文件中的包名+类名都可以从.h文件中复制,有了.c文件之后可以把.h文件删掉,把.c文件的头文件改为

#include <jni.h>

即c文件如下

#include <jni.h>
JNIEXPORT jstring JNICALL Java_cn_cun_jnihello_MainActivity_getStringFromNative(JNIEnv *env, jobject obj){
    char *str = "String from native C";
    return (*env)->NewStringUTF(env, str);
}

 

char *str="String from native C";是为了用C语言中的字符数组代替Java中的字符串,NewStringUTF()方法是把字符数组转化为Java中的字符串

 

.h文件的生成也可以用第二种方法:

在terminal中进入到java目录下,输入javah -jni “包名.类名”

javah -jni cn.cun.jnihello.MainActivity

Android Studio开发Jni教程-入门篇

即可在java下生成.h文件

Android Studio开发Jni教程-入门篇

之后在jni文件夹下新建.c文件,输入如下代码。

#include <jni.h>
JNIEXPORT jstring JNICALL Java_cn_cun_jnihello_MainActivity_getStringFromNative(JNIEnv *env, jobject obj){
    char *str = "String from native C";
    return (*env)->NewStringUTF(env, str);
}


汇总:
1.新建工程
2.在main下新建jni文件夹
3.在项目gradle.properties文件中加上android.useDeprecatedNdk=true
4.local.properties中加入你自己ndk和sdk的路径:
5.在app文件夹下的build.gradle中的defaultConfig里加入如下代码
ndk{    
   moduleName "hello"//生成的so文件名
}
6.在jni文件夹下新建Android.mk
LOCAL_PATH :=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=hello   
LOCAL_SRC_FILES :=hello.c
include $(BUILD_SHARED_LIBRARY)
7.新建Application.mk
APP_MODULES := hello
APP_ABI := armeabi
8.在MainActivity中编写代码
static {//固定写法,表示我们要加载的资源文件为libhello.so
    System.loadLibrary("hello");
}
//声明一个本地方法,用native关键字修饰
 public native String getStringFromNative();
9.编译MainActivity,生成.h文件
10.在jni下新建C文件
#include <jni.h>
JNIEXPORT jstring JNICALL Java_cn_cun_jnihello_MainActivity_getStringFromNative(JNIEnv *env, jobject obj){
    char *str = "String from native C";
    return (*env)->NewStringUTF(env, str);
}
11.编译C文件,生成so文件
12.运行项目