JNI踩过的一些坑

JNI一些坑

pthread创建的子线程没有 _JNIEnv

因为_JNIEnv 是根据线程相关的,所以pthread创建的native线程必须要调用JavaVMd->AttachCurrentThread(&env,NULL)方法绑定该线程到虚拟机,然后再线程结束的时候调用DetachCurrentThread()方法解除绑定

void *connect(void *arg) {
    SokcetClient *client = static_cast<SokcetClient *>(arg);
    _JNIEnv *env;
    JavaVM *vm= client->getJavaVM();
    client->getJavaVM()->AttachCurrentThread(&env, NULL);
    sockaddr_in in;
    bzero(&in, sizeof(in));
    in.sin_port = htons(client->getPort());
    in.sin_addr.s_addr = inet_addr(client->getIp());
    in.sin_family = AF_INET;
    int result = connect(client->getSocketFtd(), reinterpret_cast<const sockaddr *>(&in),
                         sizeof(in));
    if (result < 0) {
        LOGV(" ip = %s |  port = %d", client->getIp(), client->getPort());
        LOGV("socket connect error result = %d", result);
        jmethodID  errorId = env->GetMethodID(client->getJclass(),"connectFailed","()V");
        env->CallVoidMethod(client->getJobj(),errorId,NULL);
        return NULL;
    }
    jmethodID id = env->GetMethodID(client->getJclass(), "connectSuccess", "()V");
    env->CallVoidMethod(client->getJobj(), id, NULL);
    client->setConnect(true);
    client->receiveMsg();
    client->getJavaVM()->DetachCurrentThread();
    return NULL;
}

子线程调用FindClass查找自定义的类为NULL

这是因为通过AttachCurrentThread附加到虚拟机的线程在查找类时只会通过系统类加载器进行查找,不会通过应用类加载器进行查找,因此可以加载系统类,但是不能加载非系统类,如自己在java层定义的类会返回NULL。

    env->FindClass("xxx/xxx/xxx");//在子线程这样调用是会返回NULL
        env->FindClass("java/lang/String");//ok

目前本人的解决办法是,在主线程时去findClass然后当做global保存起来

    //下边在主线程执行
    jclass  cls = env->FindClass("xxx/xxx/xxx");
    jclass  globalCls = static_cast<jclass>(env->NewGlobalRef(cls));//一定要使用NewGlobalRef

这个地方一定要使用NewGlobalRef 将它保存,因为JNI默认的生命周期只有方法体内,这个方法执行完,就会被回收。所以用把它保存成全局引用 然后再不用的时候 调用方法释放掉

    env->DeleteGlobalRef(globalCls);

线程运行完会崩溃

pthread创建完的线程运行完会Crash,这是因为他是有返回值的!!!(Java写习惯之后完全不会注意这一点。)

void* run(void *args){
    ///省略很多代码
    return NULL //这句话一定要写。如果没有返回值就返回NULL
}
void test(){    
    pthread threadId;
    pthread_create(&threadId,NULL,run,NULL);//这个run的方法体会返回void*的返回值
}



线程的回收

pthread创建的线程运行完如果不进行回收会一直占用一些资源,可以在线程运行时调用方法pthread_detach(pthread_self()),这样它会再运行完进行回收。也可以使用join方法,具体可以查资料

void* run(void *args){
    ///省略很多代码
  pthread_detach(pthread_self())//释放掉资源,一定要在运行的方法体内执行pthread_self()会获取当前的线程Id
    return NULL 
}
void test(){    
    pthread threadId;
    pthread_create(&threadId,NULL,run,NULL);
}


Java的ByteBuffer.allocateDirect

public static ByteBuffer allocateDirect(int capacity)分配新的直接字节缓冲区。 
新缓冲区的位置将为零,其界限将为其容量,其标记是不确定的。无论它是否具有底层实现数组,其标记都是不确定的。 
参数:
capacity - 新缓冲区的容量,以字节为单位

allocateDirect方法直接使用操作系统来分配Buffer。因而它将提供更快的访问速度。与native交互会有更高的执行速度。但是在使用的时候发现他会在byte[]头部和尾部会多加几个字节,导致数据异常。暂时未发现为什么会这样。

与之对应的还有一个allocate方法。这个方法可以正常使用。

Java中的byte在C中怎么处理

众所周知 Java中的byte是一个字节,在C中只占一个字节的只有char。在调用到jni中byte会被转为jbyte ,这个是一个typedef具体定义如下

typedef int8_t   jbyte;

所以对应的C类型就是int8_t。其实int8_t也是一个typedef

typedef __int8_t      int8_t;

___int8_t也是一个typedef

typedef signed char __int8_t;

如何将char[4]转成一个int32_t的数字

char header[4];
int number  = (header[0]<<24)|(header[1]<<16)|(header[2]<<8)|header[3]-11;

利用位用算:

​ 第一位左移24位 XXXXXXXX 00000000 00000000 00000000

​ 第二位左移16位 00000000 XXXXXXXX 00000000 00000000

​ 第三位左移8位 00000000 00000000 XXXXXXXX 00000000

​ 第四个保持不变 00000000 00000000 00000000 XXXXXXXX

然后将上边4个进行按位与操作 就是

                        XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX 

这样就变成一个int型的整数。

一个int32_t的数字转为1个char[4]

char header[4];
int32_t number= X;
header[0] = number>>24&0xFF;
header[1] = number>>16&0xFF;
header[2] = number>>8&0xFF;
header[3] =number&0xFF;

首先一个整数他表示为

                XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX 

首8位就是向右移24位然后就变成了

​ 00000000 00000000 00000000 XXXXXXXX

然后与0xFF进行&(同位均为1就是1 否则就是0)运算

                        00000000 00000000 00000000 XXXXXXXX 

​                                       &           

​                        00000000 00000000 00000000 11111111

这就就会取到前8位的数。

然后第二个8位就右移16位然后与0xFF进行&

                        00000000 00000000 XXXXXXXX XXXXXXXX 

​                                       &           

​                        00000000 00000000 00000000 11111111

这样就能取到第二个8位

剩下的以此类推。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351