Android JNI编程(八)——体验AS2.2.2编写Jni程序、Java调C、C调Java函数、将C代码中的Log打印至Logcat
版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/。
不得不说在AS2.2以上的版本进行开发就一个字——爽,在2.0上使用jni出现各种问题现在果断使用新版本开发了。
使用AS2.0创建Jni项目: 使用AndroidStudio编写第一个JNI程序
一:编写Jni程序
1. 我们创建一个项目,将Include C++ Support勾选一路Next直到Finish 这样就可以全自动为我们创建好一个带有Jni的项目了
编译过程中需要你为这个工程配置Ndk的路径,只需在
Project Sturcture配置即可(不懂看文章上面的链接文章)。如果你第一次使用,那么他会提示你需要下CMake你也只需下载即可
2. 在AS2.2以上版本中,使用的是CMake来编译我们的C代码
CMake官网:https://cmake.org/
Google官方文档:Jni开发文档
CMakeLists.txt
//以#号开头都是注释,这里把它们都删了也就更清楚了
cmake_minimum_required(VERSION 3.4.1)
#生成的库名
add_library( native-lib
#编译的文件路径
src/main/cpp/native-lib.c )
find_library( log-lib
log )
target_link_libraries( native-lib
${log-lib} )
3. 默认创建的是cpp文件,这里将它改成.c文件同时修改CMakeLists.txt中的src/main/cpp/native-lib.c文件后缀名,改完之后记得sync一下
4. 在build.gradle中配置生成的so库类型,记得sync一下
android {
//。。。
defaultConfig {
//。。。
ndk {
moduleName "native-lib"//生成so库的名字,与CMakeLists中的一致
abiFilters "armeabi", "armeabi-v7a", "x86"//cpu类型
}
}
}
二:Java调用C代码
1. 创建一个类,存放native函数
public class JniInterface {
//加载so文件
static {
System.loadLibrary("native-lib");
}
/*返回一个字符串*/
public native String stringFromJNI();
}
2. 使用Alt+Enter来自动生成对应的Jni函数,确定之后会在main/jni目录中创建一个C文件,我们只需要将里面的方法复制到我们的native-lib.c文件中
1. native-lib.c文件
#include <jni.h>
/**
* jstring: 返回值
* Java_全类名_方法名
* JNIEnv* env: 里面有很多方法
* jobject jobj: 谁调用了这个方法就是谁的实例
*/
jstring Java_com_zsy_ajni_JniInterface_stringFromJNI(JNIEnv *env, jobject jobj) {
char *str = "Hello from C";
return (*env)->NewStringUTF(env, str);
}
3. 在我们的MainActivity中调用stringFromJNI()便可以调用成功了。
然后在项目的build/intermediates/cmake/debug/obj/这里就可以看到编译生成的so库了
三:C调用Java代码
1. 在JniInterface类中继续增加我们的native方法,让java调用callbackAdd();函数,然后Jni调用add(int a,int b);函数
public class JniInterface {
//加载so文件
static {
System.loadLibrary("native-lib");
}
/**
* 返回一个字符串
*/
public native String stringFromJNI();
/**
* 当执行这个函数的时候,让C代码调用add(int a, int b)
*/
public native void callbackAdd();
/**
* 这个函数让C调用
*/
public int add(int a, int b) {
Log.d("TAG", "我是add函数被C调用了a = " + a + ";b = " + b);
return a + b;
}
}
2. MainActivity中调用callbackAdd()函数
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JniInterface jni = new JniInterface();
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(jni.stringFromJNI());
//调用这个函数,让C调用我们的add函数
jni.callbackAdd();
}
}
3. 接下来就是重点了,在native-lib.c文件继续中生成callbackAdd()对应的Jni函数代码中来调用我们的add(int a, int b)函数
/**
* C代码调用Java JniInterface类中的add(int a, int b)函数
*/
void Java_com_zsy_ajni_JniInterface_callbackAdd(JNIEnv *env, jobject jobj) {
//1、得到字节码 FindClass(env,类的全路径);
jclass class = (*env)->FindClass(env, "com/zsy/ajni/JniInterface");
//2、得到方法 GetMethodID(env,jclass,方法名,方法签名);
//方法签名是要调用的方法名对应的签名
jmethodID methodID = (*env)->GetMethodID(env, class, "add", "(II)I");
//3、实例化该类
jobject obj = (*env)->AllocObject(env, class);
//4.调用Java类中的方法
//成功调用add方法;第二三个参数是我们调用add方法传入的参数。value就是add方法的返回值
jint value = (*env)->CallIntMethod(env, obj, methodID, 45, 23);
}
4. 上面代码中,我们需要去获取一个方法的签名那我们来获取一下:
将工程build让类生成对应的.class文件。然后使用
Terminal进入到app\build\intermediates\classes\debug>目录下,执行javap -s 类的全路径descriptor就是方法签名。
![]()
类的全路径可以通过在类名上右键,点击Copy Reference即可
5. 运行我们的程序,就可以在控制看到Log了也就证明方法调用成功
四:在C代码中打印Log至Logcat
上面我们是在Java函数中打印的Log,现在我们来让C中也可以打印Log
1.配置C代码打印Log
- 在build.gradle中添加日志打印so库
ndk {
//....
//就是依赖ndk工具包中的一个liblog.so文件
ldLibs "log"
}
- 在.c文件中添加如下代码,看注释就明白了
#include <android/log.h>
//日志标签,随意定义
#define LOG_TAG "TAG"
//Debug等级
#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
//Info等级
#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
//Error等级
#define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
- 配置好了,我们就可以来用了
#include <jni.h>
#include <android/log.h>
//日志标签,随意定义
#define LOG_TAG "TAG"
//Debug等级
#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
//Info等级
#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
//Error等级
#define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
/**
* jstring: 返回值
* Java_全类名_方法名
* JNIEnv* env: 里面有很多方法
* jobject jobj: 谁调用了这个方法就是谁的实例
*/
jstring Java_com_zsy_ajni_JniInterface_stringFromJNI(JNIEnv *env, jobject jobj) {
char *str = "Hello from C";
return (*env)->NewStringUTF(env, str);
}
/**
* C代码调用Java JniInterface类中的add(int a, int b)函数
*/
void Java_com_zsy_ajni_JniInterface_callbackAdd(JNIEnv *env, jobject jobj) {
//此处代码省略,与上面代码一致
jint value = (*env)->CallIntMethod(env, obj, methodID, 45, 23);
//调用Log输出,使用与printf函数一致
LOGD("value=%d", value);
}
2. 成功运行后机可以在Logcat查看到日志信息了
在2.2.2版本上可以关联到源码跟踪源码非常方便,写Jni也是个不错的体验。Demo地址
Android JNI编程(八)——体验AS2.2.2编写Jni程序、Java调C、C调Java函数、将C代码中的Log打印至Logcat的更多相关文章
- Android JNI编程(七)——使用AndroidStudio编写第一个JNI程序
版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 1.简单介绍一下NDK和JNI NDK:NDK是Native Develop ...
- Android中JNI编程的那些事儿(1)
转:Android中JNI编程的那些事儿(1)http://mobile.51cto.com/android-267538.htm Android系统不允许一个纯粹使用C/C++的程序出现,它要求必须 ...
- 【转】Android JNI编程—JNI基础
原文网址:http://www.jianshu.com/p/aba734d5b5cd 最近看到了很多关于热补的开源项目——Depoxed(阿里).AnFix(阿里).DynamicAPK(携程)等,它 ...
- 在 JNI 编程中避免内存泄漏
JAVA 中的内存泄漏 JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏:JVM 内存中 native memory 的内存泄漏. Java H ...
- 【Android 应用开发】Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- 【Android 应用开发】Android 网络编程 API笔记 - java.net 包相关 接口 api
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- 在 JNI 编程中避免内存泄漏与崩溃
JNI 编程简介 JNI,Java Native Interface,是 native code 的编程接口.JNI 使 Java 代码程序可以与 native code 交互——在 Java 程序中 ...
- 解析Java的JNI编程中的对象引用与内存泄漏问题
JNI,Java Native Interface,是 native code 的编程接口.JNI 使 Java 代码程序可以与 native code 交互——在 Java 程序中调用 native ...
- Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
随机推荐
- spring定时器完整
介绍:在开发中,我们经常需要一些周期性就进行某一项操作.这时候我们就要去设置个定时器,Java中最方便.最高效的实现方式是用java.util.Timer工具类,再通过调度java.util.Time ...
- vue使用marked.js实现markdown转html并提取标题生成目录
html: <template> <div class="wrapper"> <div class="container"> ...
- CSS min-height不能解决垂直外边距合并问题
垂直外边距合并有一种情况是嵌套元素的垂直外边距合并,当父级元素没有设定外边距时,在顶部或者底部边缘的子元素的垂直外边距就会和父级的合并,导致父级也有了“隐形”的垂直外边距. 当父级元素的min-hei ...
- Centos下Elasticsearch安装详细教程
Centos下Elasticsearch安装详细教程 1.Elasticsearch简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于 ...
- 洛谷 P1571 眼红的Medusa
P1571 眼红的Medusa 题目描述 虽然Miss Medusa到了北京,领了科技创新奖,但是他还是觉得不满意.原因是,他发现很多人都和他一样获了科技创新奖,特别是其中的某些人,还获得了另一个奖项 ...
- Linux下vi替换字符命令操作实例
在Linux下的开发中,经常涉及到对文件里的字符进行处理,当中,对字符的替换操作也是非常的频繁. 本文以一个实际的文件为例,具体介绍了Linux下经常使用的vi替换字符命令,为相关的开发工作提供给了參 ...
- Delphi部份函数,命令,属性中文说明
Abort 函数 引起放弃的意外处理 Abs 函数 绝对值函数 AddExitProc 函数 将一过程添加到运行时库的结束过程表中 Addr 函数 返回指定对象的地址 AdjustLineBreaks ...
- Python *的下载、安装和使用
最近正在学习机器学习和深度学习,需要python,大家都知道,它是这些领域的入门语言,不多说,直接上干货!突然发现python比java好玩多了 下面就开始python的安装以及测试,python2. ...
- MFC单文档程序架构解析
MFC单文档程序架构解析 MFC单文档程序架构解析 这里我以科院杨老师的单文档程序来分析一下MFC单文档的程序架构,纯属个人见解,不当之处烦请指教! 首先我们了解到的是 图(一) theApp 是唯一 ...
- 【Codeforces Round #429 (Div. 1) B】Leha and another game about graph
[链接]点击打开链接 [题意] 给出一个连通图,并给每个点赋一个d值0或1或-1,要求选出一个边的集合,使得所有的点i要么d[i] == -1,要么 dgree[i] % 2 == d[i],dgr ...