Android:JNI强化训练
一、前言
Java本机接口(Java Native Interface (JNI))是本机编程接口,它是JDK的一部分,JNI它提供了若干的API,实现了和Java和其他通信(主要是C&C++),用于从Java程序调用C/C++,以及从C/C++程序调用Java代码。
本文旨在强化JNI的使用技巧,简单的使用可另外参考 https://www.cnblogs.com/blogs-of-lxl/p/9268732.html 的 JNI接口实现 部分。
二、Java层存储JNI层动态创建的C++对象(Java调用C++)
1.C++层的代码如下:
定义了一个食品类,里面有获取食品名称和价格的方法。
#pragma once class CFood
{
private:
char* name;
double price;
public:
CFood(char* name, double price)
{
this->name = name;
this->price = price;
} ~CFood()
{
if(name != NULL)
{
free(name);
name = NULL;
}
} const char* getName()
{
return this->name;
} double getPrice()
{
return this->price;
}
};
2.JNI层的实现代码:
通过JNI实现对c++类的调用。
(1)头文件:test_Food.h
#include <jni.h>
/* Header for class test_Food */ #ifndef _Included_test_Food
#define _Included_test_Food
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test_Food
* Method: setFoodParam
* Signature: (Ljava/lang/String;D)V
*/
JNIEXPORT void JNICALL Java_test_Food_setFoodParam
(JNIEnv *, jobject, jstring, jdouble); /*
* Class: test_Food
* Method: getName
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_test_Food_getName
(JNIEnv *, jobject); /*
* Class: test_Food
* Method: getPrice
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_test_Food_getPrice
(JNIEnv *, jobject); /*
* Class: test_Food
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_test_Food_finalize
(JNIEnv *, jobject); #ifdef __cplusplus
}
#endif
#endif
(2)代码实现:test_Food.cpp
#include "stdafx.h"
#include <stdlib.h>
#include "test_Food.h"
#include "Food.h" // jstring转string类型方法
char* jstring2string(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > )
{
rtn = (char*)malloc(alen + ); memcpy(rtn, ba, alen);
rtn[alen] = ;
}
env->ReleaseByteArrayElements(barr, ba, );
return rtn;
} // char转jstring类型方法
jstring char2Jstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, , strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
} // 给Java层CFood对象指针赋值
void setFood(JNIEnv *env, jobject thiz, const CFood* pFood)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
env->SetIntField(thiz, fid, (jint)pFood);
} // 获取Java层CFood对象指针
CFood* getCFood(JNIEnv *env, jobject thiz)
{
jclass clazz = env->GetObjectClass(thiz); //获取thiz中对象的class对象
jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); //获取Java中的mObject字段的id(mObject是Java中存储C++层对象的指针)
jint p = env->GetIntField(thiz, fid); //获取mObject的值
return (CFood*)p;
} // JNI接口:供Java调用c++ CFood类创建食品对象
JNIEXPORT void JNICALL Java_test_Food_setFoodParam
(JNIEnv *env, jobject thiz, jstring name, jdouble price)
{
//const char* tempName = env->GetStringUTFChars(name, 0);
char* tempName = jstring2string(env, name);
double tempPrice = price; CFood* pFood = new CFood(tempName, tempPrice); //根据Java层传下来的参数创建一个食品对象
setFood(env, thiz, pFood); //将创建的食品对象指针通过JNI赋值给Java层变量
} // JNI接口:供Java层调用获取食品名称
JNIEXPORT jstring JNICALL Java_test_Food_getName
(JNIEnv *env, jobject thiz)
{
CFood* pFood = getCFood(env, thiz);
const char* name = pFood->getName();
return char2Jstring(env, name);
} // JNI接口:供Java调用获取食品价格
JNIEXPORT jdouble JNICALL Java_test_Food_getPrice
(JNIEnv *env, jobject thiz)
{
CFood* pFood = getCFood(env, thiz);
return pFood->getPrice();
} // JNI接口:供Java调用析构c++对象。
JNIEXPORT void JNICALL Java_test_Food_finalize
(JNIEnv *env, jobject thiz)
{
CFood* pFood = getCFood(env, thiz);
if (pFood != NULL)
{
delete pFood;
pFood = NULL;
setFood(env, thiz, pFood);
}
}
3.Java层为了使用上述代码,引入一个新的类Food,如下:
public class Food { static {
System.loadLibrary("jniFood"); //加载JNI及c++部分代码编译生成的so
} // 用于存储C++层的对象指针
private int mObject; public Food(String name, double price) {
setFoodParam(name, price);
}
// 本地方法,在JNI实现的接口
public native void setFoodParam(String name, double price);
public native String getName();
public native double getPrice();
protected native void finalize();
// 测试
public static void main(String[] args) {
Food f1 = new Food("面包", 1.99);
Food f2 = new Food("牛奶", 3.99); System.out.println(String.format("食物:%s, 单价:%f", f1.getName(), f1.getPrice()));
System.out.println(String.format("食物:%s, 单价:%f", f2.getName(), f2.getPrice()));
}
}
其中,声明了本地方法,需要注意的是创建一个int型字段用来存放C++层对象的指针。另外需要注意的是通过本地方法finalize()来析构c++对象。
三、C++中存放Java对象(C++回调Java)
首先实现单线程的回调,始终将 JNI接口参数中的 JNIEnv * 和 jobject 一起传参使用,不作保存。
1.Java层代码:
package test1; // 内部实现一个MyPrint类
class MyPrint {
public void onPrint(String text) {
System.out.println(text);
}
} // MyFile类
public class MyFile { private MyPrint myPrint = null; static {
System.loadLibrary("jniTest"); //加载JNI动态库
} private int mObject; // MyFile类的构造函数
public MyFile() {
init(); //初始化一个C++层的CMyFile对象,并将对象的地址保存到上面的mObject变量中
} // MyFile类中的onPrint方法,调用MyPrint对象的onPrint方法
public void onPrint(String text) {
myPrint.onPrint(text);
} // 本地保存C++传过来的myPrint对象
public void setPrint(MyPrint myPrint) {
this.myPrint = myPrint;
} // 保存Java本地myPrint方法,同样在C++的创建CMyprint对象并注册到CMyFile对象中去
public void setMyPrint(MyPrint myPrint) {
setPrint(myPrint);
this.registerPrint();
} public void myPrint(String text) {
this.doPrint(text);
} // 本地方法,在JNI中实现
public native void init();
public native void registerPrint(MyPrint myPrint);
public native void doPrint(String text);
protected native void finalize(); // 测试
public static void main(String[] args) {
MyFile myFile = new MyFile(); //实例化MyFile对象,主要是C++层实例化一个CMyFile对象并将对象指针保存到Java层mObject变量
MyPrint myPrint = new MyPrint(); //实例化一个Java层MyPrint对象 myFile.setMyPrint(myPrint); //保存本地MyPrint对象,另外C++层也会创建一个CMyPrint对象并注册到CMyFile对象中去
myFile.doPrint("hello world!"); /*通过JNI接口调用C++层CMyfile对象中CMyPrint对象的onPrint方法,然后C++层中的onPrint方法又会通过传过去的 JNIEnv* 和 jobject 来获取Java对象,并回调Java层的onPrint方法*/
myFile.doPrint("again!");
myFile.doPrint("next!");
}
}
2.JNI层实现:
C++头文件:MyPrint.h
#include "stdafx.h"
#include <jni.h>
#include <stdlib.h> class CMyPrint
{
public:
jstring char2Jstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
} // 如下传递JNIEnv* 和 jobject参数来获取Java对象,然后回调
void onPrint(JNIEnv *env, jobject thiz, char* text)
{
jclass clazz = env->GetObjectClass(thiz);
jmethodID methodID = env->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V");
jstring strText = char2Jstring(env, text);
env->CallVoidMethod(thiz, methodID, strText);
}
};
C++头文件:MyFile.h
#pragma once #include "MyPrint.h" class CMyFile
{
private:
CMyPrint* pPrint;
public: ~CMyFile()
{
if (pPrint != NULL)
{
delete pPrint;
pPrint = NULL;
}
} void registerPrint(CMyPrint* pPrint)
{
this->pPrint = pPrint;
} void doPrint(JNIEnv *env1, jobject thiz, char* text)
{
pPrint->onPrint(env1, thiz, text);
}
};
JNI头文件:test1_MyFile.h
#include <jni.h>
/* Header for class test1_MyFile */ #ifndef _Included_test1_MyFile
#define _Included_test1_MyFile
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test1_MyFile
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_test1_MyFile_init
(JNIEnv *, jobject); /*
* Class: test1_MyFile
* Method: registerPrint
* Signature: (Ltest1/MyPrint;)V
*/
JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint
(JNIEnv *, jobject); /*
* Class: test1_MyFile
* Method: doPrint
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_test1_MyFile_doPrint
(JNIEnv *, jobject, jstring); /*
* Class: test1_MyFile
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_test1_MyFile_finalize
(JNIEnv *, jobject); #ifdef __cplusplus
}
#endif
#endif
JNI实现代码:
#include "stdafx.h"
#include <jni.h>
#include "MyFile.h"
#include "test1_MyFile.h"
// jstring转string类型方法
char* jstring2string(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > )
{
rtn = (char*)malloc(alen + ); memcpy(rtn, ba, alen);
rtn[alen] = ;
}
env->ReleaseByteArrayElements(barr, ba, );
return rtn;
}
// 获取Java层mObject保存的指针
CMyFile* getMyFile(JNIEnv *env, jobject thiz)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
jint p = env->GetIntField(thiz, fid);
return (CMyFile*)p;
}
// 将CMyFile对象指针保存到Java层的mObjecj变量中
void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
env->SetIntField(thiz, fid, (jint)pFile);
}
// 创建C++层的CMyFile对象,并将对象指针保存到Java层
JNIEXPORT void JNICALL Java_test1_MyFile_init
(JNIEnv *env, jobject thiz)
{
CMyFile* pFile = new CMyFile();
setMyFile(env, thiz, pFile);
}
// 创建C++层的CMyPrint对象,并注册到CMyFile对象中
JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint
(JNIEnv *env, jobject thiz)
{
CMyPrint* pPrint = new CMyPrint();
CMyFile* pFile = getMyFile(env, thiz);
pFile->registerPrint(pPrint);
}
// 调用CMyFile对象中注册的CMyPrint对象的打印方法
JNIEXPORT void JNICALL Java_test1_MyFile_doPrint
(JNIEnv *env, jobject thiz, jstring strText)
{
CMyFile* pFile = getMyFile(env, thiz);
char* pText = jstring2string(env, strText);
pFile->doPrint(env, thiz, pText);
if (pText != NULL)
{
free(pText);
pText = NULL;
}
}
// 析构C++层的CMyFile对象
JNIEXPORT void JNICALL Java_test1_MyFile_finalize
(JNIEnv *env, jobject thiz)
{
CMyFile* pFile = getMyFile(env, thiz);
if (pFile != NULL)
{
delete pFile;
pFile = NULL;
setMyFile(env, thiz, pFile);
}
}
上述的回调是在一个线程栈中完成的,那如何实现多线程的回调实现呢?由于JNIEnv *不能被缓存,只在当前线程中有效,而且JNI中接口的参数都是局部引用,当该方法栈执行完毕,局部引用就会被销毁,所以每次都要获取JNIEnv *和jobject对象参数进行回调,而可以缓存的是JavaVM*,同样应该将JavaVM*转换为全局引用再缓存,jobject也可以转换为全局引用后缓存。
继续在上面代码进行修改,共同部分就不贴了:
1. Java层代码:
package test1; class MyPrint {
public void onPrint(String text) {
System.out.println(text);
}
} public class MyFile { private MyPrint myPrint = null; static {
System.loadLibrary("jniTest");
} private int mObject; public MyFile() {
init(); //初始化一个C++层的CMyFile对象,并将对象的地址保存到上面的mObject变量中
} public void setPrint(MyPrint myPrint) {
this.myPrint = myPrint;
} public void setMyPrint(MyPrint myPrint) {
setPrint(myPrint);
this.registerPrint(myPrint); // 保存Java本地myPrint方法,且创建CMyPrint对象保存 JavaVM* , jobject 参数
}
// 本地方法
public native void init();
protected native void finalize();
public native void registerPrint(MyPrint myPrint);
public native void doPrint(String text);
// 测试
public static void main(String[] args) {
MyFile myFile = new MyFile();
MyPrint myPrint = new MyPrint();
myFile.setMyPrint(myPrint);
myFile.doPrint("hello world!");
System.out.println("等待打印结果...");
try {
Thread.sleep(20*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.JNI层实现:
CMyFile* getMyFile(JNIEnv *env, jobject thiz)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
jint p = env->GetIntField(thiz, fid);
return (CMyFile*)p;
} void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fid = env->GetFieldID(clazz, "mObject", "I");
env->SetIntField(thiz, fid, (jint)pFile);
} JNIEXPORT void JNICALL Java_test1_MyFile_init
(JNIEnv *env, jobject thiz)
{
CMyFile* pFile = new CMyFile();
setMyFile(env, thiz, pFile);
} JNIEXPORT void JNICALL Java_test1_MyFile_finalize
(JNIEnv *env, jobject thiz)
{
CMyFile* pFile = getMyFile(env, thiz);
if (pFile != NULL)
{
delete pFile;
pFile = NULL;
setMyFile(env, thiz, pFile);
}
} JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint
(JNIEnv *env, jobject thiz, jobject jPrint)
{
JavaVM* pVM = NULL;
env->GetJavaVM(&pVM); // 根据局部引用生成全局引用
JavaVM* g_pVM = (JavaVM*)env->NewGlobalRef((jobject)pVM);
jobject g_javaPrint = env->NewGlobalRef(jPrint); CMyPrint* pPrint = new CMyPrint(g_pVM, g_javaPrint); // 其中会保存 g_pVM 和 g_javaPrint
CMyFile* pFile = getMyFile(env, thiz);
pFile->registerPrint(pPrint);
} JNIEXPORT void JNICALL Java_test1_MyFile_doPrint
(JNIEnv *env, jobject thiz, jstring strText)
{
CMyFile* pFile = getMyFile(env, thiz);
char* pText = CMyPrint::jstring2string(env, strText); pFile->doPrint(pText); if (pText != NULL)
{
free(pText);
pText = NULL;
}
}
3.C++层代码:
typedef struct _ThreadParam
{
JavaVM* jvm;
jobject javaPrint;
string text;
}ThreadParam; DWORD WINAPI funproc(LPVOID lpparentet)
{
Sleep(*); ThreadParam* param = (ThreadParam*)lpparentet; JNIEnv* pEnv = NULL;
param->jvm->AttachCurrentThread((void**)&pEnv, NULL); //附加当前线程到Java(Dalvik)虚拟机(创建CMyPrint时保存了jvm) jclass clazz = pEnv->GetObjectClass(param->javaPrint); // 获取非静态方法ID
jmethodID methodID = pEnv->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V"); jstring strText = CMyPrint::char2Jstring(pEnv, param->text.c_str()); // 调用非静态方法
pEnv->CallVoidMethod(param->javaPrint, methodID, strText); if (param != NULL)
{
delete param;
param = NULL;
}
return ;
} /*
* CMyPrint 类
*/
class CMyPrint
{
private:
jobject mJavaPrintObj;
JavaVM* jvm;
public: CMyPrint(JavaVM* jvm, jobject javaPrintObj) // 创建CMyPrint对象时保存JavaVM*和jobject,可用于子线程回调
{
this->jvm = jvm;
this->mJavaPrintObj = javaPrintObj;
} ~CMyPrint()
{
JNIEnv* pEnv = NULL;
jvm->AttachCurrentThread((void**)&pEnv, NULL);
pEnv->DeleteGlobalRef(mJavaPrintObj);
pEnv->DeleteGlobalRef((jobject)jvm);
} static char* jstring2string(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > )
{
rtn = (char*)malloc(alen + ); memcpy(rtn, ba, alen);
rtn[alen] = ;
}
env->ReleaseByteArrayElements(barr, ba, );
return rtn;
} static jstring char2Jstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, , strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
} void onPrint(char* text)
{
ThreadParam* param = new ThreadParam();
param->jvm = jvm;
param->javaPrint = mJavaPrintObj;
param->text = text; HANDLE hander = CreateThread(NULL,,funproc,param,,NULL); // 创建一个子线程回调Java层的方法
}
}; /*
* CMyFile 类
*/
class CMyFile
{
private:
CMyPrint* pPrint;
public: ~CMyFile()
{
if (pPrint != NULL)
{
delete pPrint;
pPrint = NULL;
}
} void registerPrint(CMyPrint* pPrint)
{
this->pPrint = pPrint;
} void doPrint(char* text)
{
pPrint->onPrint(text);
}
};
四、JNI中的字符编码方式的完美转换
1、相关概念:
(1)、Java层使用的是16bit的unicode编码(utf-16)来表示字符串,无论中文还是英文,都是两个字节。
(2)、JNI层使用的是UTF-8编码,UTF-8是变长编码的unicode,一般ascii字符1字节,中文3字节。
(3)、C/C++使用的是原始数据,ascii就是一个字节,中文一般是GB2312编码,用两个字节表示一个汉字。
2、字符流向
(1)、Java ---> C/C++ :
这时候,Java调用的时候使用的是UTF-16编码,当字符串传递给JNI方法时,C/C++得到的输入是jstring,这时候,JNI提供了两个函数,一个是GetStringUTFChars,该函数将得到一个UTF-8编码的字符串(char*类型),另一个函数是GetStringChars,该函数将得到一个UTF-16编码的字符串(wchar_t*类型)。无论哪种结果,得到的字符串如果含有中文,都需要进一步转换为GB2312编码。
(2)、C/C++ ---> Java :
这时候,是JNI返回给Java字符串。C/C++首先应该负责把这个字符串变成UTF-8或UTF-16格式,然后通过NewStringUTF或者NewString来把它封装成jstring,返回给Java就可以了。
如果字符串中不含中文字符,只是标准的ascii码,那么使用GetStringUTFChars/NewStringUTF就可以搞定了,因为这种情况下,UTF-8编码和ascii编码是一致的,不需要转换。
如果字符串中有中文字符,那么在C/C++部分就必须进行编码转换。我们需要两个转换函数,一个是把UTf-8/-16编码转成GB2312;另一个是把GB2312转成UTF-8/-16。
这里需要说明一下:linux和win32都支持wchar,这个事实上就是宽度为16bit的unicode编码UTF-16,所以,如果我们的c/c++程序中完全使用wchar类型,那么理论上就不需要这种转换。但是实际上,我们不可能完全用wchar来取代char的,所以就目前大多数应用而言,转换仍然是必须的。
(3)、使用wide char类型来转换 :
char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16转换成gb2312
int length = (env)->GetStringLength(jstr );
const jchar* jcstr = (env)->GetStringChars(jstr, ); int clen = WideCharToMultiByte( CP_ACP, , (LPCWSTR)jcstr, length, NULL,, NULL, NULL ); char* rtn = (char*)malloc( clen ) //更正。作者原来用的是(char*)malloc( length*2+1 ),当java字符串中同时包含汉字和英文字母时,所需缓冲区大小并不是2倍关系。
int size = ;
size = WideCharToMultiByte( CP_ACP, , (LPCWSTR)jcstr, length, rtn,clen, NULL, NULL );
if( size <= )
return NULL;
(env)->ReleaseStringChars(jstr, jcstr );
rtn[size] = ;
return rtn;
} jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312转换成utf8/16
jstring rtn = ;
int slen = strlen(str);
unsigned short * buffer = ;
if( slen == )
rtn = (env)->NewStringUTF(str );
else
{
int length = MultiByteToWideChar( CP_ACP, , (LPCSTR)str, slen, NULL, );
buffer = (unsigned short *)malloc( length* + );
if( MultiByteToWideChar( CP_ACP, , (LPCSTR)str, slen, (LPWSTR)buffer, length ) > )
rtn = (env)->NewString( (jchar*)buffer, length );
}
if( buffer )
free( buffer );
return rtn;
}
Android:JNI强化训练的更多相关文章
- [转载]—— Android JNI知识点
Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互.JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 ...
- Android JNI 和 NDK
1.Android NDK 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...
- Android jni简便开发流程
<Android jni helloworld>中介绍了开发jni helloworld的步骤,本文将介绍jni简便开发流程 ① 写java代码 native 声明本地方法 ② 添加本地支 ...
- Android jni系统变量、函数、接口定义汇总
在做Android jni开发时,jni为我们提供了哪些函数.接口.变量,有时候一头雾水,今天就把jni.h中定义的所有内容列出来,供自己查阅: /* * Copyright (C) 2006 The ...
- android JNI调用(转)
Android jni开发资料--NDK环境搭建 android开发人员注意了 谷歌改良了ndk的开发流程,对于Windows环境下NDK的开发,如果使用的NDK是r7之前的版本,必须要安装Cygwi ...
- Android JNI如何调用第三方库
http://www.2cto.com/kf/201504/388764.html Android JNI找不到第三方库的解决方案 cannot load library 最近做一个jni项目,拿到的 ...
- Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)
转载请声明:原文转自:http://www.cnblogs.com/xiezie/p/5930503.html Android JNI之JAVA与C++对象建立对称关联 1.JAVA对象持有C++对象 ...
- 利用gdb 调试android jni c动态库
http://blog.dornea.nu/2015/07/01/debugging-android-native-shared-libraries/ Since I haven't done thi ...
- Android JNI使用方法
经过几天的努力终于搞定了android JNI部分,下面将我的这个小程序和大家分享一下.android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和 ...
随机推荐
- 王之泰201771010131《面向对象程序设计(java)》第十四周学习总结
第一部分:理论知识学习部分 第12章 Swing用户界面组件 12.1.Swing和MVC设计模式 a 设计模式初识b 模型—视图—控制器模式c Swing组件的模型—视图—控制器分析 12.2布局管 ...
- iso移动端input的bug解决(vue)
iso中input很奇怪,点击空白地方,键盘也不会消失,影响页面中其他功能 解决办法: 点击的元素不是input或者textarea,那么就让上一个获得焦点的输入框失去焦点. 涉及的代码: <i ...
- Python装饰器的另类用法
之前有比较系统介绍过Python的装饰器(请查阅<详解Python装饰器>),本文算是一个补充.今天我们一起探讨一下装饰器的另类用法. 语法回顾 开始之前我们再将Python装饰器的语法回 ...
- VBoxManage安装
扩展包的版本需要与VirtualBox的版本一致,通过帮助可以查看VirtualBox的版本信息,然后在http://download.virtualbox.org/virtualbox/寻找对应的版 ...
- 实验九 FBG 团队项目需求改进与系统设计
任务一 A.<项目需求规格说明书>分析 根据老师的指导以及本周所学的OOA,分析改进上周编写的<项目需求规格说明书>,发现需求项目书UML图例描述不够完善,仅仅是用例图没办法更 ...
- leecode第二百三十六题(二叉树的最近公共祖先)
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...
- 《SQL 基础教程》第八章:SQL 高级处理
本章分为两个部分: 窗口函数 GROUPING 运算符 它们用于以「窗口」为单位的排序.计算总和等任务. OLAP 函数 OLAP 定义:OLAP 是 OnLIne Analytical Proces ...
- Oracle之数组
记忆力不好,提供样例套路: 固定长度数组: declare type t_test ) ); test t_test := t_test('a', 'b', 'c', 'd', 'e'); begin ...
- python 【pandas】读取excel、csv数据,提高索引速度
问题描述:数据处理,尤其是遇到大量数据且需要for循环处理时,需要消耗大量时间,如代码1所示.通过data['trip_time'][i]的方式会占用大量的时间 代码1 import time t0= ...
- Django的admin相关
自定义admin展示的内容 根据之前已经创建好了的models from django.db import models class Person(models.Model): name = mode ...