0x01 前言

  本文讲述使用Android Studio通过静态注册、动态注册使用JNI的方法,以及加载第三方so文件的方法

0x02 Android Studio静态注册的方式使用JNI

  1. 添加native接口

public class MainActivity extends Activity implements OnClickListener {

    static{
System.loadLibrary("JniTest");
} private native int Add(double num1,double num2);
private native int Sub(double num1,double num2);
private native int Mul(double num1,double num2);
private native int Div(double num1,double num2); @Override
protected void onCreate(Bundle savedInstanceState) {

  在Java类中使用System.loadLibrary("JniTest")加载我们要写的so库名称,Add/Sub/Mul/Div这四个方法在Java类中声明就可以使用了。

  2.Build->Make Project
  验证工程中并无其他错误,并对工程进行编译,生成.class文件
  在Build/intermediates/classes/debug里面

  3.javah 生成.h文件

  cmd 进入工程目录在工程的app/src/main/java ,执行 javah com.example.caculate.MainActivity 命令,就会生成so库所需要的.h文件

  在/app/src/main/下建立jni目录,将.h拷贝进去

  4.jni目录下新建一个.c文件,完成so库的函数实现(注:这里的Android.mk文件并不需要,可以不写,用法在下面会提到)

  
  

  5.Build->Make Project  会报错,这里在android studio中添加ndk路径编译生成so文件

①在local.properties文件中增加ndk的路径

  ②在/app/目录下的build.gradle文件里增加 so的名称

ndk {
  moduleName "JniTest"
  abiFilters "armeabi", "armeabi-v7a", "x86"
}

  6.Build->Make Project 就可以编译出so文件了
  生成的so在app/Build/intermediates/ndk/debug/lib下面

 

  7.点击运行,就可以使用我们实现的so中的代码了

(注:在so库中实现在原结果基础上加2)

  使用模拟器利用busybox安装grep命令之后,我们可以通过/proc/<pid>/maps文件中保存的进程加载模块,查看我们写的so文件是否被加载了。

  8.我们JNI的实现文件

  ①com_example_caculate_MainActivity.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_caculate_MainActivity */ #ifndef _Included_com_example_caculate_MainActivity
#define _Included_com_example_caculate_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_caculate_MainActivity
* Method: Add
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_example_caculate_MainActivity_Add
(JNIEnv *, jobject, jdouble, jdouble); /*
* Class: com_example_caculate_MainActivity
* Method: Sub
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_example_caculate_MainActivity_Sub
(JNIEnv *, jobject, jdouble, jdouble); /*
* Class: com_example_caculate_MainActivity
* Method: Mul
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_example_caculate_MainActivity_Mul
(JNIEnv *, jobject, jdouble, jdouble); /*
* Class: com_example_caculate_MainActivity
* Method: Div
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_com_example_caculate_MainActivity_Div
(JNIEnv *, jobject, jdouble, jdouble); #ifdef __cplusplus
}
#endif
#endif

  ②so库中实现函数的main.c文件

#include <jni.h>
#define jintJNICALL
#ifndef _Included_com_example_caculate_MainActivity
#define _Included_com_example_caculate_MainActivity
#ifdef __cplusplus
extern "C" {
#endif JNIEXPORT jintJNICALL Java_com_example_caculate_MainActivity_Add
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 + num2+2);
} JNIEXPORT jintJNICALL Java_com_example_caculate_MainActivity_Sub
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 - num2+2);
} JNIEXPORT jintJNICALL Java_com_example_caculate_MainActivity_Mul
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 * num2+2);
} JNIEXPORT jintJNICALL Java_com_example_caculate_MainActivity_Div
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
if (num2 == 0) return 0;
return (jint)(num1 / num2+2);
}
#ifdef __cplusplus
}
#endif
#endif

0x03 Android Studio动态注册的方式使用JNI

  1.jni目录下直接编写so库中的.c文件

  JNI_Onlad会作为so库被加载后的第一个执行函数,最后通过RegisterNatives函数将JNI函数注册

#include <jni.h>
#include <stdio.h>
//#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> JNIEXPORT jint JNICALL native_Add
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 + num2 +);
} JNIEXPORT jint JNICALL native_Sub
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 - num2 +);
} JNIEXPORT jint JNICALL native_Mul
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
return (jint)(num1 * num2 +);
} JNIEXPORT jint JNICALL native_Div
(JNIEnv *env, jobject obj, jdouble num1, jdouble num2)
{
if (num2 == ) return ;
return (jint)(num1 / num2 +);
} //Java和JNI函数的绑定表
static JNINativeMethod gMethods[] = {
{"Add", "(DD)I", (void *)native_Add},
{"Sub", "(DD)I", (void *)native_Sub},
{"Mul", "(DD)I", (void *)native_Mul},
{"Div", "(DD)I", (void *)native_Div},
}; //注册native方法到java中
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods,numMethods) < ){
return JNI_FALSE;
} return JNI_TRUE;
} int register_ndk_load(JNIEnv *env)
{ return registerNativeMethods(env, "com/example/caculate/MainActivity",
gMethods,sizeof(gMethods) / sizeof(gMethods[]));
//NELEM(gMethods));
} JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
} register_ndk_load(env); // 返回jni的版本
return JNI_VERSION_1_4;
}

(注:这里Android.mk文件也不需要,在下面会提到)

  2.在Java类中添加native接口,加载JniTest.so,以及声明native函数,声明之后,函数可以直接使用。

public class MainActivity extends Activity implements OnClickListener {

    static{
System.loadLibrary("JniTest");
} private native int Add(double num1,double num2);
private native int Sub(double num1,double num2);
private native int Mul(double num1,double num2);
private native int Div(double num1,double num2); @Override
protected void onCreate(Bundle savedInstanceState) {

  

  3.make Project 生成so库,如果之前没有修改/app/build.gradle文件和在local.properties中指定ndk路径则会报错。报错参考静态使用JNI中的步骤5,指定ndk路径,并且指定so名称和输出的架构。

  

  生成成功,则在/app/build/intermediates/ndk/debug/lib目录下生成相应的so文件

  

  4.在虚拟机中运行

(注:这里的结果在原结果上加1)

0x04 Android Studio加载第三方库

  1.使用ndk生成so文件

  ①建立jni目录(随便在哪个地方)

  ②编写so库中的.c函数,这里完全使用0x03 Android Studio动态注册的方式使用JNI中的c文件,使用动态注册的方式

  ③编写Android.mk文件(单独使用ndk编译so文件的时候需要用到)

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := JniTest
LOCAL_SRC_FILES := MyJniCalc.c
LOCAL_SHARED_LIBRARIES := libandroid_runtime
include $(BUILD_SHARED_LIBRARY)

  ④使用cmd进入jni目录,执行ndk-build,生成so文件

  

  

  2.加载生成的so文件,打包进apk中

  ①我们在/app目录下建立libs目录,将我们的so文件拷贝进去

  这里可以使用ndk自己生成的so文件,也可以使用其他第三方so文件

  

  

  ②改写/app/build.gradle文件,编译的时候将so生成上图中的libJniTest.jar文件

apply plugin: 'com.android.application'

android {
compileSdkVersion
buildToolsVersion "23.0.3" defaultConfig {
applicationId "com.example.caculate"
minSdkVersion
targetSdkVersion
} buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
} sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir 'src/main/libs'
} task nativeLibsToJar(type: Zip, description: "create a jar archive of the native libs") {
destinationDir file("$projectDir/libs")
baseName "libJniTest"
extension "jar"
from fileTree(dir: "libs", include: "**/*.so")
into "lib"
} tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn(nativeLibsToJar)
} } dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:18.0.0'
compile files('libs/libJniTest.jar')
}

  其中into "lib" 是生成的jar文件存放的目录,include: "**/*.so" 为所依赖的so文件

  ③在Java类中添加so的接口

public class MainActivity extends Activity implements OnClickListener {

    static{
System.loadLibrary("JniTest");
} private native int Add(double num1,double num2);
private native int Sub(double num1,double num2);
private native int Mul(double num1,double num2);
private native int Div(double num1,double num2); @Override
protected void onCreate(Bundle savedInstanceState) {

  

  4.Make Project,点击运行

  这里不需要指定ndk路径,so库的名称也不需要在/app/build.gradle中指定,在Android.mk中指定

(注:这里结果在原结果上加1)

0x05 总结

  这里是Android Studio中的使用方法,刚开始也是各种百度,弄得很复杂,在后面的学习中也懂得了很多,所以今天将使用方法重新整理了一遍,希望大家可以不要浪费太多时间。静态注册方法主要要用javah生成.h文件,显得比较复杂,每次添加函数都要重新生成.h文件,不过如果知道函数名称的格式也可以不生成.h文件,实践中,在jni目录下只有main.c文件也是可以运行成功的。动态注册方法直接在函数中注册比较容易动态扩展,但是需要对注册的数据类型有所了解,可以参考静态方法生成的.h文件中有对应的数据类型。加载第三方库感觉是最实用的,不管是so文件加密,还是使用不开源的so库,都需要加载已经生成的so文件。

   注意/app/build.gradle中的sdk版本要改成自己android studio中sdk有的版本,如果没有也可根据android studio提示下载。

  代码下载:https://github.com/LycorisGuard/android

   最后编辑于2016.9.17

Android Studio使用JNI的更多相关文章

  1. Android studio 下JNI编程实例并生成so库

    Android studio 下JNI编程实例并生成so库 因为公司需要为Android相机做美颜等图像后期处理,需要使用JNI编程,最近学了下JNI,并且在Android Studio下实现了一个小 ...

  2. Android studio 配置JNI环境

    Android studio配置jni开发环境,主要配置是两个build文件,以及新建一个jni文件,放c代码. 代码如下1: apply plugin: 'com.android.model.app ...

  3. android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测

    目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...

  4. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    相信很多小伙伴都在android开发中遇到调用jni的各种巨坑,因为我们不得不在很多地方用到第三方库so文件,然而第三方官方通常都只会给出ADT环境下的集成方式,而谷歌亲儿子android studi ...

  5. Android Studio中JNI程序的单步调试和日志打印

    近日有个算法(检测碰撞)需要用C++实现,目的是IOS和ANDROID中共享同一段程序. 下面说说android调用这段程序过程中遇到的一些事情.(过程中网上搜索了一些相关文章,大部分说的是eclip ...

  6. Android Studio使用JNI和NDK进行开发

    想要学习一下在Android Studio中进行JNI的开发,文章挺多的,但是几乎没有一个完整的说明的,中间总是有一两步漏掉.分享技术就应该完整的让读者学会,藏着掖着不是君子所为.对于那些故意含糊过去 ...

  7. android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!

    目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. ------------------------------------- ...

  8. Android Studio新建Jni工程

    2.2版本的Android Studio支持新建Jni工程,不用再像以前自己构建工程目录,首先把自己的升级自己的AS到2.2以上 然后打开Tools->Andorid->SDK manag ...

  9. windows android studio 编译Jni动态库

    项目需要,折腾了半天搞定windows android studio环境编译Jni动态库,现记录下来. 准备安装环境: 1. android studio 下载地址是http://www.androi ...

随机推荐

  1. 一道program test题目

    前天去面试的一道上机测试题(凭记忆写的题目)Question:给定输入整数\(\left( k \right)\),找到最小的自然数\(n\left( {n \ge 1} \right)\),使得下列 ...

  2. php 文件file常用的操作

    多是代码形式呈现,更多的信息可以查看php api文档,搜索Filesystem; //创建文件夹 $path_find = AppRoot . '/Temp/excel_curl'; //查找的路径 ...

  3. JS面向对象组件(六) -- 拖拽功能以及组件的延展

    HTML部分 <div id="div1"></div> <div id="div2"></div> CSS部分 ...

  4. CDB和PDB基本管理

    CDB和PDB基本管理 这篇文章主要介绍CDB和PDB的基本管理,资料来源oracle官方. 基本概念: Multitenant Environment:多租户环境 CDB(Container Dat ...

  5. Getting and installing the PEAR package manager

    Windows After you have downloaded and installed PHP, you have to manually execute the batch file loc ...

  6. 页面性能测试&提升方式

    性能测试包括:web系统页面测试.web系统后台测试 2种方式来提升你的web 应用程序的速度: ● 减少请求和响应的往返次数 ● 减少请求和响应的往返字节大小. 详细的看此文http://www.5 ...

  7. Linux系统上安装Python

    1.下载Python安装包,官网下:http://www.python.org/getit/ http://jingyan.baidu.com/article/eae07827f7f2d01fec54 ...

  8. 部署测试环境(ubuntu+mysql+tomcat)

    背景:入职新公司,广州这边没有测试开发环境,需要自己搭建一个:要求ubuntu+mysql+tomcat有具体版本要求:   2015/4/13 下载Ubuntu12.04 http://mirror ...

  9. 网易实习笔试真题C/C++

    刚做的时候根本就没有想到解题思路,刚好看到了别人的思路,自己写了一下.里面对unordered_map及vector二维数组的建立很灵活,另外区别了一下map,unordered_map,hash_m ...

  10. String内存陷阱简介

    String 方法用于文本分析及大量字符串处理时会对内存性能造成一些影响.可能导致内存占用太大甚至OOM. 一.先介绍一下String对象的内存占用 一般而言,Java 对象在虚拟机的结构如下:•对象 ...