JNI通过线程c回调java层的函数
1、参看博客:http://www.jianshu.com/p/e576c7e1c403
Android JNI 篇 - JNI回调的三种方法(精华篇)
2、参看博客:
JNI层线程回调Java函数关键点及示例
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层的方法,最近将线程环境关闭。
我们来看下程序的框架:
我们来看下程序的代码:
- package im.weiyuan.com.jni;
- public class Sdk {
- static {
- System.loadLibrary("hello");
- }
- public Sdk() {
- }
- //单例
- private static class SdkHodler {
- static Sdk instance = new Sdk();
- }
- public static Sdk getInstance() {
- return SdkHodler.instance;
- }
- //回调到各个线程
- public interface OnSubProgressListener {
- public int onProgressChange(long total, long already);
- };
- //c层回调上来的方法
- public int onProgressCallBack(long total, long already) {
- //自行执行回调后的操作
- System.out.println("total:"+total);
- System.out.println("already:"+already);
- return 1;
- }
- //调到C层的方法
- public native void nativeDownload();
- }
然后
(一) 第二步:make project一下,目的就是编译成对应的class文件。然后根据生成的class文件,利用javah生成对应的 .h头文件。
(一) 第三步:
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文件夹,里面有生成好的头文件:
在这个头文件中就自动帮助我们生成了函数的声明
第五步:
在jni目录下新建一个 .c文件。来实现头文件里面声明的方法。我的叫im_weiyuan_com_jni_Sdk.c
我们来看下程序的代码:
- //
- // Created by wei.yuan on 2017/6/13.
- //
- #include <jni.h>
- #include <string.h>
- #include <pthread.h>
- #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 <jni.h>
- #include <string.h>
- #include <android/log.h>
- #include <time.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <dlfcn.h>
- #include <assert.h>
- #include <android\log.h>
- #include <errno.h>
- #include <pthread.h>
- static void* native_thread_exec(void* arg){
- JNIEnv *env;
- int mNeedDetach = -;
- //获取当前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) != ) {
- return;
- }
- mNeedDetach = JNI_TRUE;
- }
- //通过全局变量g_obj 获取到要回调的类
- jclass javaClass = (*env)->GetObjectClass(env, g_obj);
- if (javaClass == ) {
- // 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,,);
- //释放当前线程
- if(mNeedDetach) {
- (*g_VM)->DetachCurrentThread(g_VM);
- }
- //释放你的全局引用的接口,生命周期自己把控
(*env)->DeleteGlobalRef(env, g_obj);
g_obj = NULL;
- env = NULL;
- }
- JNIEXPORT void JNICALL 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))!=){
- return ;
- }
- return;
- }
其中:
- JNIEXPORT void JNICALL 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
(一) 在gradle.propertes中添加
android.useDeprecatedNdk=true
在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函数的代码:
- package im.weiyuan.com.jni;
- import android.app.Activity;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(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的目录如下所示:
JNI通过线程c回调java层的函数的更多相关文章
- jni不通过线程c回调java的函数 --总结
1.JNIEnv类型是一个指向全部JNI方法的指针.该指针只在创建它的线程有效,不能跨线程传递 2.JavaVM是虚拟机在JNI中的表示,一个JVM中只有一个JavaVM对象,这个对象是线程共享的. ...
- jni不通过线程c回调java的函数
整个工程的项目如下: 1.项目的思路是在activity中启动MyService这个服务,在服务中调用 JniScsManger类中的本地方法startNativeScsService,在 start ...
- Jni层回调java代码【转】
本文转载自:http://www.linuxidc.com/Linux/2014-03/97562.htm JNI是Java Native Interface的缩写,是Java平台的重要特性,使得Ja ...
- Java层与Jni层的数组传递(转)
源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...
- Android开发实践:Java层与Jni层的数组传递
转载:http://www.linuxidc.com/Linux/2014-03/97561.htm Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是 ...
- Jni本地多线程回调Java函数,env->findClass()失败。
遇到的问题,Native层本地多线程回调Java函数时env->findClass()失败. 前面的代码是这样的在 JNI_OnLoad记录全局变量g_vm static JavaVM* g_v ...
- [Android Webkit]JNI基础及Java层与C++层的交互
1. JNI 注册 1.1. JNI的基础结构 JAVA == JNI == Native Code JNI(Java Native Interface)是Java与Native ...
- JNI中修改(基本类型)参数并返回到Java层使用
最近在JNI相关项目中遇到一个问题:在Java层传入多个int类型的参数,在jni层修改参数值或地址之后重新返回到Java层.这应该算是基本知识了,尤其是基本类型的参数往往看似简单,所以在之前学习jn ...
- Android JNI 由C/C++本地代码向Java层传递数据
最近做的Android项目需要调用C代码,进行串口通信及与硬件设备通信,因此要用到JNI,其中本地代码需要向Java层返回三个参数,分别为 参数一:int型: 参数二: 通信指令,本地代码中为unsi ...
随机推荐
- SpringBoot 集成 Mybatis(三)
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.增加持久化层 <dependency> <groupId>mysql< ...
- Java实现 LeetCode 661 图片平滑器(暴力)
661. 图片平滑器 包含整数的二维矩阵 M 表示一个图片的灰度.你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元 ...
- Java实现 LeetCode 493 翻转对
493. 翻转对 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对. 你需要返回给定数组中的重要翻转对的数 ...
- Java实现 蓝桥杯VIP 算法训练 递归求二进制表示位数
问题描述 给定一个十进制整数,返回其对应的二进制数的位数.例如,输入十进制数9,其对应的二进制数是1001,因此位数是4. 样例输入 一个满足题目要求的输入范例. 9 样例输出 与上面的样例输入对应的 ...
- Java实现 LeetCode 46 全排列
46. 全排列 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2] ...
- Java实现 洛谷 P1280 尼克的任务
import java.util.Scanner; public class Main { public static class edg{ private int to; private int n ...
- Java实现奇偶数排序
1 问题描述 给定一个整数数组,请调整 数组中数的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分.要求时间复杂度为O(n). 2 解决方案 2.1 一头一尾指针往中间扫描法 pack ...
- java实现第六届蓝桥杯奇怪的数列
奇怪的数列 从X星截获一份电码,是一些数字,如下: 13 1113 3113 132113 1113122113 - YY博士经彻夜研究,发现了规律: 第一行的数字随便是什么,以后每一行都是对上一行& ...
- 关于virgo-tomcat-server-3.6.0.RELEASE服务的启动
1.先查看程序是否启动,如果已经启动可以执行第3步的操作进行关闭. [user01@ ~]$ # ps -ef|grep java //查看virgo-tomcat-server的java进程是否存在 ...
- 基于Azure IoT开发.NET物联网应用系列-全新的Azure IoT架构
物联网技术已经火了很多年了,业界各大厂商都有各自成熟的解决方案.我们公司主要搞新能源汽车充电,充电桩就是物联网技术的最大应用,车联网.物联网.互联网三网合一.2017年的时候重点研究过Azure Io ...