美文网首页
JNI踩过的一些坑

JNI踩过的一些坑

作者: 会飞的羽天羽 | 来源:发表于2019-07-10 14:23 被阅读0次

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位

剩下的以此类推。

相关文章

  • JNI踩过的一些坑

    JNI一些坑 pthread创建的子线程没有 _JNIEnv 因为_JNIEnv 是根据线程相关的,所以pthre...

  • 踩坑JNI

    使用Android studio3.4.1,即最新版时。使用cmake调用opencv3.2的jni库,出现 go...

  • 安卓开发通过JNI调用本地方法

    今天开发中第一次接触到对jni方法的调用,特此记录在开发踩过的坑。 java通过JNI调用c/c++编写的本地方法...

  • JNI入门踩坑

    要用Java 8 。10把javah这个工具给移除了

  • 交互设计师所要避免的几个坑

    前言 工作中难免会踩到几个坑,即使现在不踩以后还会踩,只有踩过才会深刻记住,踩过说明爱过!但是踩过的坑必须把坑填满...

  • 大数据爬坑收录

    爬出过的坑 大数据运维过程就是一个踩坑的过程。如下分享一些踩过的坑,以供参考。 Hive Spark Flink ...

  • 一些踩过的坑

    使用Ant design(react)时遇到了 其中第二句话可以看成const Header = Layout.H...

  • vue踩过的坑

    vue踩过的坑

  • [Android ]JNI 入门踩坑

    大家好,我系苍王。 以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。 [Androi...

  • D1094:踩坑的价值最大化

    是人就会踩坑,不踩坑理论上就不属于人类,踩坑是人之常情,能回头站在坑边反思,才是对踩过的的坑价值最大化的体现,要不...

网友评论

      本文标题:JNI踩过的一些坑

      本文链接:https://www.haomeiwen.com/subject/jzhhkctx.html