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位
剩下的以此类推。