Android开发实践:JNI层线程回调Java函数示例

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://ticktick.blog.51cto.com/823160/1358558

JNI是Java Native Interface的缩写,是Java平台的重要特性,使得Java代码可以方便地与C/C++代码编译生成的动态链接库进行交互。本文主要给出一份示例代码(工程文件见附件),描述如何在Android的JNI层开启一个线程,并在线程中回调Java层的函数。

代码主要分为Java层(java代码)和JNI层(c语言代码),首先看看Java层的代码(Native.java)。

如上所示,Java层与JNI层的接口代码主要封装在Native类中,该类定义了三个native函数,分别完成jni库的初始化,调用jni层开启线程,调用jni层关闭线程等功能。并且提供一个回调函数(onNativeCallback),供jni层调用,并在回调函数中打印count的值。

再看看JNI层是如何开启线程并回调Java层的(native.c),关键的地方都在代码中进行了注释:

native C实现:

1. 头文件包含和全局变量的定义

2. 初始化函数的实现

3. 开启关闭线程的实现

4. 线程的实现(关键)

native C++实现

#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#include <unistd.h>

#define TAG "JniNative"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)

JavaVM *gJavaVM;
jobject gJavaObj;
int volatile gIsThreadStop = 0;
static const char *classPath = "com/jni/test/JniNativeCallback";

static void* native_thread_exec(void *arg)
{
    LOGI("nativeThreadExec");
    JNIEnv *env;
    //get env From gJavaVm
    gJavaVM->AttachCurrentThread(&env,NULL);
    //get Java class by classPath
//    jclass thiz = env->FindClass(classPath);
    jclass thiz = env->GetObjectClass(gJavaObj);
    //get Java method from thiz
    jmethodID nativeCallback = env->GetMethodID(thiz,"nativeCallback","(I)V");
    int count = 0;
    while(!gIsThreadStop)
    {
        sleep(2);
        //env->CallVoidMethod(thiz,nativeCallback,count++);
        env->CallVoidMethod(gJavaObj,nativeCallback,count++);
    }
    gJavaVM->DetachCurrentThread();
    LOGI("thread stoped");
}

JNIEXPORT void JNICALL native_init(JNIEnv *env,jobject thiz)
{
    LOGI("native_init");
    gIsThreadStop = 0;
    env->GetJavaVM(&gJavaVM);
    gJavaObj = env->NewGlobalRef(thiz);
}

JNIEXPORT void JNICALL native_thread_start(JNIEnv *env,jobject jthiz)
{
    LOGI("native_thread_start");
    gIsThreadStop = 0;

pthread_t id;
    if(pthread_create(&id,NULL,native_thread_exec,NULL)!=0)
    {
        LOGI("native thread create fail");
        return;
    }

LOGI("native thread creat success");
}

JNIEXPORT void JNICALL native_thread_stop(JNIEnv *env,jobject jthiz)
{
    gIsThreadStop = 1;
    LOGI("native_thread_stop");
}

static JNINativeMethod methods[] = {
        {"nativeInit","()V",(void*)native_init},
        {"nativeThreadStart","()V",(void*)native_thread_start},
        {"nativeThreadStop","()V",(void*)native_thread_stop}
};

jint JNI_OnLoad(JavaVM *vm, void *reserve)
{
    LOGI("JNI_OnLoad");
    JNIEnv *env;
    if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK)
    {
        return -1;
    }

jclass jthiz = env->FindClass(classPath);

if(env->RegisterNatives(jthiz,methods,sizeof(methods)/sizeof(methods[0]))<0)
    {
        return -1;
    }
    return JNI_VERSION_1_4;
}

由上述代码可以看到,JNI层通过pthread库完成了线程的创建,需要特别注意的是,JNI层的线程中,必须通过全局的JavaVM来获取到环境变量,也必须通过全局的jobject获取java类对象,从而找到java端的函数,进行回调。

时间: 2024-10-11 11:38:22

Android开发实践:JNI层线程回调Java函数示例的相关文章

Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

转:Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

Android中关于JNI 的学习(三)在JNI层訪问Java端对象

前面两篇文章简介了JNI层跟Java层的一些相应关系,包含方法名,数据类型和方法名称等,相信在理论层面.可以非常好地帮助我们去了解JNI在Native本地开发中的作用,对JNI的一些概念也有了一个初步的认识,因为表达能力或者理解还是有限,有些地方讲得不是非常清楚.假设各位朋友有认为云里雾里,欢迎大家留言一起学习. 概念上的理解有助于我们更好地认识JNI.而一些实际点的样例则可以更好地帮我们从代码上去掌握并应用JNI. 在第一篇文章,我们是从一个小样例来入门学习的,在当中,我们通过JNI层函数返回

【转】 Android 开发 之 JNI入门 - NDK从入门到精通

原文网址:http://blog.csdn.net/shulianghan/article/details/18964835 NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub - https://github.com/han1202012/NDKHelloworld.git -- Java传递参数给C语言实例程序 : GitHub - https://github.com/han1202012/NDKParameterPassing.git --C语言回调Java方法示例

Android开发实践:利用ProGuard进行代码混淆

由于Android的代码大都是Java代码,所以挺容易被反编译的,好在Android ADT为我们集成了混淆代码的工具,一来可以混淆我们的代码,让程序被反编译后基本看不懂,另外还能起到代码优化的作用.发布项目前,建议打开Android的代码混淆功能. Android ADT主要通过ProGuard工具来提供代码混淆,网上也有挺多博客文章讲这个的,但感觉很多都介绍得太过于复杂,这里我就以问答的方式来更加简洁地介绍下ProGuard吧. 1. ProGuard是什么 ProGuard是一个工具,用来

Android开发实践:WIFI连接功能的封装

在上一篇文章<Android开发实践:WIFI扫描功能的封装>介绍了如何利用Andriod的API实现WIFI的扫描,本文则重点讲述一下如何连接WIFI吧,在此,也给出一个封装WIFI连接过程的类,提供简单的接口以供在各个代码工程中复用. 与WIFI扫描类似,WIFI的连接同样是一个耗时的过程,所以需要放到线程中执行,通过回调来通知调用者连接结果.该回调接口的定义如下: public interface WifiConnectListener { public void OnWifiConne

Android开发实践:由new Handler()说开去

最近面试一些Android开发的应聘者,除了基本的Activity生命周期等基础问题以外,我一般还会问如下两个问题: (1) Service与Thread有什么区别? (2) 在Activity里new Handler()和在自己创建的Thread中new Handler()有什么区别? 第一个问题其实是一个伪命令,因为Service是Android四大组件之一,而Thread只是Java提供的一个封装了线程管理的工具类,无论是Activity还是Service,都可以通过Thread来创建一个

Android开发实践:以“专业”的态度处理多线程

刚开始学一门编程语言的时候,我总是会有一种困惑,怎样让自己的代码看起来更"专业"?很多时候,我们可以照着教材实现一些基本的功能,比如用Socket发送/接收几个字符,写一个线程完成某个异步任务,但是在实际的项目中,往往不那么简单,比如需要设计Socket通信协议,需要处理Socket的连接异常断开,需要考虑在线程阻塞的情况下如何正常退出和释放资源等等,关于这些"实战经验",前面的文章也有所涉及,以后有空准备再开个专题跟大家分享探讨一下,今天先简单地说说怎样更&quo

Android开发实践:Android交叉编译工具链的使用

前面2篇文章分别介绍了Android NDK编译的命令行参数,以及如何在任意目录使用Android.mk来编译本地c/c++代码,Andriod.mk和ndk-build只不过是Android官方提供了一套封装过的Android交叉编译环境而已,其实,你可以不用它,而直接通过传统的Makefile文件来编译你的c/c++代码的,本文即介绍如何直接通过传统的Makefile文件来编译可用于Android平台的库文件. 经常搞嵌入式开发的朋友对于交叉编译环境应该并不陌生,说白了,就是一组运行在x86