jni c 回调 java_JNI通过线程c回调java层的函数

1、参看博客:http://www.jianshu.com/p/e576c7e1c403

Android JNI 篇 - JNI回调的三种方法(精华篇)

2、参看博客:

http://blog.csdn.net/fu_shuwu/article/details/41121741

3 http://blog.csdn.net/u010402982/article/details/48199487

核心的关键点:

三、本地线程中调用java对象

问题1:

JNIEnv是一个线程相关的变量

JNIEnv 对于每个 thread 而言是唯一的

JNIEnv *env指针不可以为多个线程共用

解决办法:

但是java虚拟机的JavaVM指针是整个jvm公用的,我们可以通过JavaVM来得到当前线程的JNIEnv指针.

可以使用javaAttachThread保证取得当前线程的Jni环境变量

static JavaVM *gs_jvm=NULL;

gs_jvm->AttachCurrentThread((void **)&env, NULL);//附加当前线程到一个Java虚拟机

jclass cls = env->GetObjectClass(gs_object);

jfieldID fieldPtr = env->GetFieldID(cls,"value","I");

问题2:

不能直接保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它。

解决办法:

用env->NewGlobalRef创建一个全局变量,将传入的obj(局部变量)保存到全局变量中,其他线程可以使用这个全局变量来操纵这个java对象

注意:若不是一个 jobject,则不需要这么做。如:

jclass 是由 jobject public 继承而来的子类,所以它当然是一个 jobject,需要创建一个 global reference 以便日后使用。

而 jmethodID/jfieldID 与 jobject 没有继承关系,它不是一个 jobject,只是个整数,所以不存在被释放与否的问题,可保存后直接使用。

总结:创建两个全局的变量一个是JavaVM 虚拟机环境jvm

另外一个全局变量是jobj对象

然后创建一个线程,使用全局的jvm获得与该线程一一对应的env,通过env和全局的jobj对象,创建java层的对象,调用java层的方法,最近将线程环境关闭。

我们来看下程序的框架:

e6b017a525c855dbe72636fb2d510a8e.png

我们来看下程序的代码:

packageim.weiyuan.com.jni;public classSdk {static{

System.loadLibrary("hello");

}publicSdk() {

}//单例

private static classSdkHodler {static Sdk instance = newSdk();

}public staticSdk getInstance() {returnSdkHodler.instance;

}//回调到各个线程

public interfaceOnSubProgressListener {public int onProgressChange(long total, longalready);

};//c层回调上来的方法

public int onProgressCallBack(long total, longalready) {//自行执行回调后的操作

System.out.println("total:"+total);

System.out.println("already:"+already);return 1;

}//调到C层的方法

public native voidnativeDownload();

}

然后

(一)   第二步:make project一下,目的就是编译成对应的class文件。然后根据生成的class文件,利用javah生成对应的 .h头文件。

ee1c4591009d80bdc413c3e35028f152.png

(一)   第三步:

Cmd终端进入到你新建的android工程的src/main目录下:我的目录是:

F:\JNI\app\src\main

执行命令:

Javah -d jni -classpath D:\android_sdk_ndk\sdk\platforms\android-21\android.jar;..\..\build\intermediates\classes\debug im.weiyuan.com.jni.Sdk

其中: D:\android_sdk_ndk\sdk\是你sdk的路径

im.weiyuan.com.jni.Sdk是你对应的

就是你声明的native函数所在的包名加上类名。

就会发现在main目录下多了一个jni文件夹,里面有生成好的头文件:

150bed997315f07532ea7acd21d61aac.png

在这个头文件中就自动帮助我们生成了函数的声明

第五步:

在jni目录下新建一个 .c文件。来实现头文件里面声明的方法。我的叫im_weiyuan_com_jni_Sdk.c

我们来看下程序的代码:

//

//Created by wei.yuan on 2017/6/13.//#include #include#include#include"im_weiyuan_com_jni_Sdk.h"#include"im_weiyuan_com_jni_Sdk_OnSubProgressListener.h"#include"im_weiyuan_com_jni_Sdk_SdkHodler.h"JavaVM*g_VM;

jobject g_obj;

#include#include#include#include#include#include#include#include#include#include#include#include

static void* native_thread_exec(void*arg){

JNIEnv*env;int mNeedDetach = -1;//获取当前native线程是否有没有被附加到jvm环境中

int getEnvStat = (*g_VM)->GetEnv(g_VM, (void **) &env,JNI_VERSION_1_6);if (getEnvStat ==JNI_EDETACHED) {//如果没有, 主动附加到jvm环境中,获取到env

if ((*g_VM)->AttachCurrentThread(g_VM, &env, NULL) != 0) {return;

}

mNeedDetach=JNI_TRUE;

}//通过全局变量g_obj 获取到要回调的类

jclass javaClass = (*env)->GetObjectClass(env, g_obj);if (javaClass == 0) {//LOGI("Unable to find class");

(*g_VM)->DetachCurrentThread(g_VM);return;

}//获取要回调的方法ID

jmethodID javaCallbackId = (*env)->GetMethodID(env, javaClass,"onProgressCallBack", "(JJ)I");if (javaCallbackId ==NULL) {//LOGI("Unable to find method:onProgressCallBack");

return;

}//执行回调

(*env)->CallIntMethod(env, g_obj, javaCallbackId,100,100);//释放当前线程

if(mNeedDetach) {

(*g_VM)->DetachCurrentThread(g_VM);

}

//释放你的全局引用的接口,生命周期自己把控

(*env)->DeleteGlobalRef(env, g_obj);

g_obj = NULL;

env =NULL;

}

JNIEXPORTvoidJNICALL Java_im_weiyuan_com_jni_Sdk_nativeDownload

(JNIEnv*env , jobject thiz){//JavaVM是虚拟机在JNI中的表示,等下再其他线程回调java层需要用到

(*env)->GetJavaVM(env, &g_VM);//生成一个全局引用保留下来,以便回调

g_obj = (*env)->NewGlobalRef(env, thiz);//此处使用c语言开启一个线程,进行回调,这时候java层就不会阻塞,只是在等待回调

pthread_t thread_id;if(( pthread_create(&thread_id,NULL, native_thread_exec,NULL))!=0){return;

}return;

}

其中:

JNIEXPORT voidJNICALL Java_im_weiyuan_com_jni_Sdk_nativeDownload

(JNIEnv *env , jobject thiz)就是在im_weiyuan_com_jni_Sdk.h头文件中系统自动生成的

(一)   第五步:配置ndk的路径

在 local.properties 文件中设置ndk的路径:

sdk.dir=D\:\\android_sdk_ndk\\sdk

ndk.dir=D\:\\android_sdk_ndk\\android-ndk-r10e

6328efcd474c470bbf1d2370165e66d2.png

(一)   在gradle.propertes中添加

android.useDeprecatedNdk=true

d3d13364c35818ca03af64ce3b45a530.png

在app目录下的 build.gradle中设置库文件名(生成的so文件名):

找到 defaultConfig 这项,在里面添加如下内容:

ndk{

moduleName "hello"  //设置库(so)文件名称

abiFilters "armeabi", "armeabi-v7a", "x86"

}

这里    hello必须和

static {

System.loadLibrary("hello");

}中的名字是对应的,abiFilters是指定生成那种平台下的so库,对应于eclipse中的Aplication.mk文件中的内容。编译,并运行。界面上就会显示从native方法传过来的值。

在c代码中

"onProgressCallBack", "(JJ)I"

这里调用上层java函数的时候,使用到了函数的签名:

如何得到函数的签名了:

如何查看函数的签名:

如果当前的工程存放的目录在F盘下的JNI目录:

进入到工程的F:\JNI\app\build\intermediates\classes\debug 目录下

执行命令:

Javap  -s  im.weiyuan.com.jni.Sdk

其中im.weiyuan.com.jni是包名,Sdk是类名

F:\JNI\app\build\intermediates\classes\debug> javap -s im.weiyuan.com.jni.Sdk

Compiled from "Sdk.java"

public class im.weiyuan.com.jni.Sdk {

public im.weiyuan.com.jni.Sdk();

descriptor: ()V

public static im.weiyuan.com.jni.Sdk getInstance();

descriptor: ()Lim/weiyuan/com/jni/Sdk;

public int onProgressCallBack(long, long);

descriptor: (JJ)I

public native void nativeDownload();

descriptor: ()V

static {};

descriptor: ()V

}

在activity中我们可以调用native函数的代码:

packageim.weiyuan.com.jni;importandroid.app.Activity;importandroid.support.v7.app.AppCompatActivity;importandroid.os.Bundle;public class MainActivity extendsActivity {

@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);//调用native的方法

Sdk.getInstance().nativeDownload();

}

}

我们来看下程序的运行结果:

06-13 15:38:40.070 14935-15032/? I/System.out: total:100

android studio 的代码地址:

http://download.csdn.net/detail/jksfkdjksdfjkjk/9869331

生成的so的目录如下所示:

654765cea0ee705c6fb28cf7aa90cdfa.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值