dc学习之基于Android Studio的jni开发总结
2015-12-23 10:16
537 查看
AS中jni技术 学习笔记
最近在学习AS下的jni开发技术,现将自己的学习笔记整理出来,望可以帮助刚学习jni的童鞋,大家有什么不懂的地方,提出来,我们一起学习,一起进步。在本篇文章中,借鉴了一些大神们的例子,在此给出链接地址:借鉴大神的资料我将从以下步骤来整理学习笔记:
AS的环境配置,及简单的程序实现。
java调用c/c++代码的实现。
c/c++调用java代码的实现。
AS jni开发的环境配置
在环境配置中,我们需要达到的目标:1. 创建一个新工程,添加本地接口
2. 配置NDK中所需环境
3. 实现简单的java调用c/c++,“dc,你好”程序。
好的,废话不多说,新建工程
我新建了一个名叫JNISimpleDemo的工程,在工程的com.example.jnisimpledemo.view目录中新建了一个名叫DCHello的类,我们在这个类中,添加native方法,代码如下:public class DCHello { private static final String libSoName = "DCHello"; /** * 载入JNI生成的so库文件 */ static { System.loadLibrary(libSoName); } /** * 该方法为native方法. */ public native String say(); }
接下来,我们要把这个本地的方法生成头文件;首先,我们要通过AS的MakeProject
生成classes文件,它对应的文件路径在C:\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\build\intermediates\classes\debug\com\example\jnisimpledemo中(这个是我的文件路径);接着,进入关键步骤,利用javah -d jni命令生成c的头文件。
第一步:进入AS的终端Terminal。
我们可以进入“C:\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\src\main>”目录,这样再运用命令产生头文件,可以确保jni文件夹正好在main文件夹下面。第二步:生成“.h”头文件
操作命令:”javah -d jni -classpath E:\zycSDK\platforms\android-23\android.jar;….\build\intermediates\classes\debug com.example.jnisimpledemo.view.DCHello”当然,在生成头文件的过程中,要根据自己的文件路径设置。
第三步:看看我们的头文件
ok,至此,我们的头文件已经生成了,并且目录结构为:,
打开文件,我们可以看到:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_jnisimpledemo_view_DCHello */ #ifndef _Included_com_example_jnisimpledemo_view_DCHello #define _Included_com_example_jnisimpledemo_view_DCHello #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_jnisimpledemo_view_DCHello * Method: say * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_jnisimpledemo_view_DCHello_say (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
其中“Java_com_example_jnisimpledemo_view_DCHello_say”就是本地方法在头文件中的表示了。
“java”代表此方法来源于java的标识头。
“com_example_jnisimpledemo_view_DCHello_say”代表say方法在工程中的路径。
我们在jni文件夹中新建一个sayHello.c文件用于测试:
#include <com_example_jnisimpledemo_view_DCHello.h> #include <jni.h> JNIEXPORT jstring JNICALL Java_com_example_jnisimpledemo_view_DCHello_say (JNIEnv * env, jobject jObj){ jstring str = (*env)->NewStringUTF(env, "dc 你好!"); return str; }
第四步:配置NDK环境
现在,我们不妨运行一下,看看会发生什么?Error:Execution failed for task ':app:compileDebugNdk'. > Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.
这是什么鬼?他让我们设置“android.useDeprecatedNdk=true”在“gradle.properties”中。
设置好后,我们再来Run一下,又出错了?
Error:Execution failed for task ':app:compileDebugNdk'. > NDK not configured. Download the NDK from http://developer.android.com/tools/sdk/ndk/.Then add ndk.dir=path/to/ndk in local.properties. (On Windows, make sure you escape backslashes, e.g. C:\\ndk rather than C:\ndk)
这个是“NDK not configured“,NDK没有配置,这时,如果我们还没有下载NDK,我们可以到下列地址下载NDK:
Android NDK http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86.zip http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86_64.zip http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86.tar.bz2 http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86_64.tar.bz2 http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86.tar.bz2 http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86_64.tar.bz2 http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86.zip http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86_64.zip http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86.tar.bz2 http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86_64.tar.bz2 http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86.tar.bz2 http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86_64.tar.bz2 http://dl.google.com/android/ndk/android-ndk-r10-cxx-stl-libs-with-debug-info.zip
在下载完对应我们机型的ndk后,双击运行会生成
文件夹,然后在local.properties文件中配置ndk的路径为:”ndk.dir=E\:\\zycSDK\\android-ndk-r10d”(我的ndk路径为:E:\zycSDK\android-ndk-r10d)。
接下来,我们需要在app\build.gradle文件夹中加入一些配置:
defaultConfig { ndk { moduleName "DCHello" ldLibs "log" abiFilters "armeabi", "armeabi-v7a", "x86" } applicationId "com.example.jnisimpledemo" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" }
其中,”DCHello“就是生成的”so“库名称;ldLibs 配置为允许日志打印;abiFilters 指定了三种abi的so库。
我们再执行RebuildProject
,发现又出错了?
make.exe: *** No rule to make target `C:\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\build\intermediates\ndk\debug\obj/local/armeabi-v7a/objs/DCHello/C_\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\src\main\jni', needed by `C:\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\build\intermediates\ndk\debug\obj/local/armeabi-v7a/objs/DCHello/C_\Users\Administrator\AndroidStudioProjects\JNISimpleDemo\app\src\main\jni\sayHello.o'. Stop.
Error:Execution failed for task ':app:compileDebugNdk'. > com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'E:\zycSDK\android-ndk-r10d\ndk-build.cmd'' finished with non-zero exit value 2
从网上查了资料发现在Sodino这里有解释,解决方法为“在Windows下NDK一个bug,当仅仅编译一个文件时会出现此问题,解决方法就是再往jni文件夹加入一个空util.c文件即可。”
那么,我们再次编译吧,发现生成了我们想要的so库!
看这里
终于看到了胜利的曙光!
ok,我们来运行,看效果:
java调用C/C++代码实现
在代码实现这一功能上,我们通过一个计算加运算的小例子来练习一下。首先看一下效果:
很简单的一个加法运算,我们先来看native方法:
/** * Copyright © 2015 Zyc. All rights reserved. * * @Project: DCJNIAdd * @Package: com.example.dcjniadd.view * @author: zyc * @date: 2015年12月22日10:42 * @version: V1.0 */ public class NativeAddM { private static final String libSoName = "NativeAddM"; /** * 载入JNI生成的so库文件 */ static { System.loadLibrary(libSoName); } /** * 该方法为native方法. * <p/> * 实现加法功能 * * @param x 加数 * @param y 加数 * @return x+y 的结果 */ public native int add(int x, int y); }
然后在jni文件夹中建了main.c,Operate.c和Operate.h文件,代码分别如下:
#include <string.h> #include <android/log.h> #include <jni.h> #include "Operate.h" /** * Java 中 声明的native add 方法的实现 * * jint x 参数X * jint y 参数Y */ jint Java_com_example_dcjniadd_view_NativeAddM_add(JNIEnv *env, jobject thiz, jint x, jint y) { return add(x,y); }
#include "Operate.h" /** * C 实现的 加法 */ int add(int x, int y) { return x+y; }
#include <string.h> #include <jni.h> int add(int x, int y);
这样再加上java端的布局就可以实现一个加运算的应用了。(在最后面提供源代码下载)
c/c++调用java代码的实现
先看效果:首先拿出核心代码实现(源码后面有下载):
#include "Provider.h" #include <android/log.h> extern JNIEnv *jniEnv; jclass TestProvider; jobject mTestProvider; jmethodID getTime; jmethodID sayHello; int GetProviderInstance(jclass obj_class); /** * 初始化 类、对象、方法 */ int InitProvider() { __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 1"); if (jniEnv == NULL) { return 0; } TestProvider = (*jniEnv)->FindClass(jniEnv, "com/example/ccalljavademo/view/TestProvider"); if (TestProvider == NULL) { return -1; } __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 2 ok"); if (GetProviderInstance(TestProvider) != 1) { (*jniEnv)->DeleteLocalRef(jniEnv, TestProvider); return -1; } __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 3 ok"); getTime = (*jniEnv)->GetStaticMethodID(jniEnv, TestProvider, "getTime", "()Ljava/lang/String;"); if (getTime == NULL) { (*jniEnv)->DeleteLocalRef(jniEnv, TestProvider); (*jniEnv)->DeleteLocalRef(jniEnv, mTestProvider); return -2; } __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 4 ok"); sayHello = (*jniEnv)->GetMethodID(jniEnv, TestProvider, "sayHello", "(Ljava/lang/String;)V"); if (sayHello == NULL) { (*jniEnv)->DeleteLocalRef(jniEnv, TestProvider); (*jniEnv)->DeleteLocalRef(jniEnv, mTestProvider); (*jniEnv)->DeleteLocalRef(jniEnv, getTime); return -3; } __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 5 ok"); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "InitProvider Begin 6"); return 1; } int GetProviderInstance(jclass obj_class) { if (obj_class == NULL) { return 0; } jmethodID construction_id = (*jniEnv)->GetMethodID(jniEnv, obj_class, "<init>", "()V"); if (construction_id == 0) { return -1; } mTestProvider = (*jniEnv)->NewObject(jniEnv, obj_class, construction_id); if (mTestProvider == NULL) { return -2; } return 1; } /** * 获取时间 ---- 调用 Java 方法 */ void GetTime() { int result = InitProvider(); if (result != 1) { return; } jstring jstr = NULL; char *cstr = NULL; __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime Begin"); jstr = (*jniEnv)->CallStaticObjectMethod(jniEnv, TestProvider, getTime); cstr = (char *) (*jniEnv)->GetStringUTFChars(jniEnv, jstr, 0); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Success Get Time from Java , Value = %s", cstr); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "GetTime End"); (*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr); (*jniEnv)->DeleteLocalRef(jniEnv, jstr); } /** * SayHello ---- 调用 Java 方法 */ void SayHello() { int result = InitProvider(); if (result != 1) { return; } jstring jstrMSG = NULL; jstrMSG = (*jniEnv)->NewStringUTF(jniEnv, "Hi,I'm From C"); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello Begin"); (*jniEnv)->CallVoidMethod(jniEnv, mTestProvider, sayHello, jstrMSG); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "SayHello End"); (*jniEnv)->DeleteLocalRef(jniEnv, jstrMSG); }
来看需要学习的知识:
1.TestProvider = (*jniEnv)->FindClass(jniEnv, “com/example/ccalljavademo/view/TestProvider”); 找java中位于“com.example.ccalljavademo.view”下的TestProvider 类;
2.
得到静态方法id:
getTime = (*jniEnv)->GetStaticMethodID(jniEnv, TestProvider, “getTime”, “()Ljava/lang/String;”);
得到非静态方法id:
sayHello = (*jniEnv)->GetMethodID(jniEnv, TestProvider, “sayHello”, “(Ljava/lang/String;)V”);
3.创建新的对象:
jmethodID construction_id = (*jniEnv)->GetMethodID(jniEnv, obj_class, “”, “()V”);
mTestProvider = (*jniEnv)->NewObject(jniEnv, obj_class, construction_id);
4.C调用java中的方法:
jstring jstr = NULL;
char *cstr = NULL;
调用静态的方法:
jstr = (*jniEnv)->CallStaticObjectMethod(jniEnv, TestProvider, getTime);
cstr = (char *) (*jniEnv)->GetStringUTFChars(jniEnv, jstr, 0);
调用非静态的方法:
jstring jstrMSG = NULL;
jstrMSG = (*jniEnv)->NewStringUTF(jniEnv, “Hi,I’m From C”);
(*jniEnv)->CallVoidMethod(jniEnv, mTestProvider, sayHello, jstrMSG);
ok,可以看出c/c++调用java写的不是很好,并且在学习中也借鉴了很多大神的资料。我正在努力中,会一直更新的,最后附上源码。
点击下载
相关文章推荐
- Android学习路线指南
- Android编程下拉菜单spinner用法小结(附2则示例)
- Android实现截屏并保存操作功能
- android 之控件篇
- Android监测程序压入后台及从后台返回
- Android library中为什么不能使用switch-case语句访问资源ID
- Android随手笔记44之JSON数据解析
- Android 开始
- 记一次Android下载过程的内存优化
- Android中类ListPreference使用介绍
- Android之双指操作
- android小知识
- Android学习笔记45之gson解析json
- Android开源项目分类汇总
- Android应用开发中单元测试分析
- Android编程开发之Spinner控件用法实例分析
- Android Native 实用型开发指南
- Android Studio上用真机调试时,无法查看Logcat日志信息解决方法
- java android 转ios积累
- Android端立体电子相册的制作