Android Studio使用JNI
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的更多相关文章
- Android studio 下JNI编程实例并生成so库
Android studio 下JNI编程实例并生成so库 因为公司需要为Android相机做美颜等图像后期处理,需要使用JNI编程,最近学了下JNI,并且在Android Studio下实现了一个小 ...
- Android studio 配置JNI环境
Android studio配置jni开发环境,主要配置是两个build文件,以及新建一个jni文件,放c代码. 代码如下1: apply plugin: 'com.android.model.app ...
- android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测
目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...
- 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案
相信很多小伙伴都在android开发中遇到调用jni的各种巨坑,因为我们不得不在很多地方用到第三方库so文件,然而第三方官方通常都只会给出ADT环境下的集成方式,而谷歌亲儿子android studi ...
- Android Studio中JNI程序的单步调试和日志打印
近日有个算法(检测碰撞)需要用C++实现,目的是IOS和ANDROID中共享同一段程序. 下面说说android调用这段程序过程中遇到的一些事情.(过程中网上搜索了一些相关文章,大部分说的是eclip ...
- Android Studio使用JNI和NDK进行开发
想要学习一下在Android Studio中进行JNI的开发,文章挺多的,但是几乎没有一个完整的说明的,中间总是有一两步漏掉.分享技术就应该完整的让读者学会,藏着掖着不是君子所为.对于那些故意含糊过去 ...
- android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!
目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. ------------------------------------- ...
- Android Studio新建Jni工程
2.2版本的Android Studio支持新建Jni工程,不用再像以前自己构建工程目录,首先把自己的升级自己的AS到2.2以上 然后打开Tools->Andorid->SDK manag ...
- windows android studio 编译Jni动态库
项目需要,折腾了半天搞定windows android studio环境编译Jni动态库,现记录下来. 准备安装环境: 1. android studio 下载地址是http://www.androi ...
随机推荐
- android事件系列-onTouch事件与手势操作
提示记忆:应用流程:在Activity中对控件执行 view.setOnTouchListener( OnTouchListener i);实现里面的OnTouchListener 接口中的方法,重点 ...
- myisam 与innodb的区别
myisam 与innodb的区别 frm结构 fri索引 frd数据 innodb:一个表一个文件:frm文件 所有的innodb表,都使用表空间储存, 数据和索引的保存文件不同,myisam 分开 ...
- 【解题报告】HDU -1142 A Walk Through the Forest
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1142 题目大意:Jimmy要从办公室走路回家,办公室在森林的一侧,家在另一侧,他每天要采取不一样的路线 ...
- android.os.NetworkOnMainThreadException解决
很早就知道Android4.0以后,要把耗时的网络操作放在多线程中,处理方法有: 1) setContentView(R.layout.activity_main)下面加上如下代码 if (andro ...
- 什么是REST?以及RESTful的实现
什么是REST? REST (REpresentation State Transfer) 描述了一个架构样式的网络系统,比如 web 应用程序.它首次出现在 2000 年 Roy Fielding ...
- hql查询语句 内存中的情况,fetch迫切查询关键字
Classes.java package cn.itcast.hiberate.sh.domain; import java.io.Serializable; import java.util.Set ...
- 使用命令行设置svn忽略列表
Windows 上的 TortoiseSVN 设置 svn 的忽略列表是非常方便的,但是在Mac OS X上,好用的图形化 svn 客户端都有点儿贵,比如 Versions 和 CornerStone ...
- linux笔记_20150417_ubuntu 常见问题_文件_音乐播放器
最近在学习ubuntu的过程中,遇到了一些问题,就记下来了它的解决办法.以希望对你也有用. ),至少保证周围局域网内用户可以访问.至于配置文件,内容比较少,反正对我来讲能用就ok了~不知道会不会很弱 ...
- Java正则表达式获取网页所有网址和链接文字
; pos1= urlContent.indexOf(strAreaBegin)+strAreaBegin.length(); pos2=urlContent.inde ...
- stardict
1.下载词典文件: 2.把下载到的文件移动到/tmp目录下 # mv stardict-*.bz2 /tmp 3.解压缩 # tar jxf stardict-oxford-gb-2.4.2.tar. ...