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

http://stackoverflow.com/questions/31979965/after-updating-android-studio-to-version-1-3-0-i-am-getting-ndk-integration-is

在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层的函数的更多相关文章

  1. jni不通过线程c回调java的函数 --总结

    1.JNIEnv类型是一个指向全部JNI方法的指针.该指针只在创建它的线程有效,不能跨线程传递 2.JavaVM是虚拟机在JNI中的表示,一个JVM中只有一个JavaVM对象,这个对象是线程共享的. ...

  2. jni不通过线程c回调java的函数

    整个工程的项目如下: 1.项目的思路是在activity中启动MyService这个服务,在服务中调用 JniScsManger类中的本地方法startNativeScsService,在 start ...

  3. Jni层回调java代码【转】

    本文转载自:http://www.linuxidc.com/Linux/2014-03/97562.htm JNI是Java Native Interface的缩写,是Java平台的重要特性,使得Ja ...

  4. Java层与Jni层的数组传递(转)

    源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...

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

    转载:http://www.linuxidc.com/Linux/2014-03/97561.htm Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是 ...

  6. Jni本地多线程回调Java函数,env->findClass()失败。

    遇到的问题,Native层本地多线程回调Java函数时env->findClass()失败. 前面的代码是这样的在 JNI_OnLoad记录全局变量g_vm static JavaVM* g_v ...

  7. [Android Webkit]JNI基础及Java层与C++层的交互

    1. JNI 注册 1.1. JNI的基础结构       JAVA == JNI == Native Code      JNI(Java Native Interface)是Java与Native ...

  8. JNI中修改(基本类型)参数并返回到Java层使用

    最近在JNI相关项目中遇到一个问题:在Java层传入多个int类型的参数,在jni层修改参数值或地址之后重新返回到Java层.这应该算是基本知识了,尤其是基本类型的参数往往看似简单,所以在之前学习jn ...

  9. Android JNI 由C/C++本地代码向Java层传递数据

    最近做的Android项目需要调用C代码,进行串口通信及与硬件设备通信,因此要用到JNI,其中本地代码需要向Java层返回三个参数,分别为 参数一:int型: 参数二: 通信指令,本地代码中为unsi ...

随机推荐

  1. 公有继承中派生类Student对基类Person成员的访问 代码参考

    #include <iostream> #include <cstring> using namespace std; class Person { private: char ...

  2. 不可不知的辅助测试的Fiddler小技巧

    在以前的博文中,时常有分享Fiddler的一些使用技巧,今天再贴下. Fiddler抓包工具使用详解 利用Fiddler拦截接口请求并篡改数据 Fiddler使用过程中容易忽略的小技巧 Mock测试, ...

  3. C# 委托浅析

    C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针.委托(Delegate) 是存有对某个方法的引用的一种引用类型变量.引用可在运行时被改变. 委托(Delegate)特别用于实现 ...

  4. centos7部署ELK测试

    一.背景 学习ELK,动手实验. 参考:https://blog.csdn.net/qq_33406938/article/details/80307679 二.环境 虚拟机一台,已安装jdk1.8, ...

  5. Rocket - debug - TLDebugModuleInner - Abstract Command Decoding & Generation

    https://mp.weixin.qq.com/s/0zKSTktxgzo5uCUphqaWSQ 介绍抽象命令的解码和生成. 1. accessRegisterCommandReg accessRe ...

  6. Spring 内部方法调用失效问题(AOP)

    AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成.内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效. 解决办法 方式一 ...

  7. MethodHandle(方法句柄)系列之一:MethodHandle和MethodType

        阅读此文章的作者建议先了解java反射和动态代理.       java7中为间接调用方法引入了新的api,其中最关键的是java.lang.invoke包,即方法句柄.我们可以看成是java ...

  8. Java实现 LeetCode 292 Nim游戏

    292. Nim 游戏 你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头. 拿掉最后一块石头的人就是获胜者.你作为先手. 你们是聪明人,每一步都是最优解 ...

  9. Java实现 蓝桥杯VIP 算法提高 聪明的美食家

    算法提高 聪明的美食家 时间限制:1.0s 内存限制:256.0MB 问题描述 如果有人认为吃东西只需要嘴巴,那就错了. 都知道舌头有这么一个特性,"由简入奢易,由奢如简难"(据好 ...

  10. 第七届蓝桥杯JavaC组国(决)赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.平方末尾 能够表示为某个整数的平方的数字称为"平方数" 比如,25,64 虽然无法立即说出某个数是平方数,但经常可 ...