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位

剩下的以此类推。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。