------------------------------------------------
jni中c文件要引入的路径:
project->properties->C/C++ General -> Paths and Symbols -> Includes ->
E:\Android\Tools\android-ndk-r9-windows-x86\android-ndk-r9\sources\cxx-stl\system\include
E:\Android\Tools\android-ndk-r9-windows-x86\android-ndk-r9\toolchains\arm-linux-androideabi-4.6\prebuilt\windows\lib\gcc\arm-linux-androideabi\4.6\include
E:\Android\Tools\android-ndk-r9-windows-x86\android-ndk-r9\toolchains\arm-linux-androideabi-4.6\prebuilt\windows\lib\gcc\arm-linux-androideabi\4.6\include-fixed
E:\Android\Tools\android-ndk-r9-windows-x86\android-ndk-r9\platforms\android-18\arch-arm\usr\include
------------------------------------------------
android工程删除jni
1、删除jni目录。
2、删除工程目录下.cproject文件
3、删除工程目录下的.project文件里的CDT相关项
4、把该工程从eclipse中删除:注意只是从工程中删除,不是从磁盘中删除。
5、把项目移到别的目录下,重新添加到eclipse工程中。
------------------------------------------------
1.在Java代码中声明本地方法必须有"native"标识符,native修饰的方法,在Java代码中只作为声明存在。
package com.example.s_jnidemo;
public class Example {
public native String jni_test(String str);
static {
System.loadLibrary("S_JniDemo");
}
}
2.在dos命令窗口中定位到工程所在的目录下,用javac编译该类:$javac HelloWorld.java
3.利用javah -jni产生头文件:$javah -jni HelloWorld
"-jni"为默认参数,可有可无.
*4.2版本:在编译生成class文件后,要在本文件package包的上一级目录下,
执行命令:javah -classpath . -jni packagename.classname。
包名之间用.分隔开
4.头文件中方法的参数
JNIEnv *:JNIEnv结构包括JNI函数表。
jobject:取决于该方法是静态还是实例方法(static or an instance method)。当本地方法作为一个实例方法时,第二个参数相当于对象本身,即this. 当本地方法作为一个静态方法时,指向所在类
5.调用GetStringUTFChars,把一个Unicode字串转成UTF-8格式字串
char *strt = (char *)env->GetStringUTFChars(prompt, NULL);
const jchar *jcstr = env->GetStringChars(prompt, 0);
上述声明中,有isCopy参数,当该值为JNI_TRUE,将返回str的一个拷贝;为
JNI_FALSE将直接指向str的内容。 注意:当isCopy为JNI_FALSE,不要修改返回值,不
然将改变java.lang.String的不可变语义。
一般会把isCopy设为NULL,不关心Java VM对返回的指针是否直接指向
有两个函数:GetStringLength/GetStringUTFLength,前者是Unicode编码长度,后者
是UTF编码长度。
GetStringUTFRegion很有用,因为你不能修改GetStringUTFChars返回值,所以需要另
外malloc/strcpy之后,再操作返回值,耗时费力,不如直接使用GetStringUTFRegion来
的简洁、高效。
6.调用ReleaseStringUTFChars释放GetStringUTFChars中分配的内存(Unicode -> UTF-8转换的原因)。
env->ReleaseStringChars(prompt, jcstr);
env->ReleaseStringUTFChars(prompt, strt);
记住在调用GetStringChars之后,要调用ReleaseStringChars做释放,不管在调用
GetStringChars时为isCopy赋值JNI_TRUE还是JNI_FALSE,因不同JavaVM实现的原因,
ReleaseStringChars可能释放内存,也可能释放一个内存占用标记(isCopy参数的作用,从
GetStringChars返回一个指针,该指针直接指向String的内容,为了避免该指针指向的内
容被GC,要对该内存做锁定标记)
7.把jni中的输出信息显示到Android的logcat窗口上
1)在mk文件的最后一行之前添加:LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
2)在.c/.cpp文件中添加:#include <android/log.h>
3)输出时调用:__android_log_print(ANDROID_LOG_INFO, "日志标签", "输出内容");
4)eg:__android_log_print(ANDROID_LOG_INFO, "MyJni", "%s, fun=%s, line=%d\n", "my print info", __FUNCTION__, __LINE__);
8.Java与JNI基本类型的映射关系表:
Java | Native(jni.h) |
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
Object | jobject |
9.签名
1)基本签名:
Type Signature | Java Type | Native Type |
Z | boolean | jboolean |
B | byte | jbyte |
C | char | jchar |
S | short | jshort |
I | int | jint |
J | long | jlong |
F | float | jfloat |
D | double | jdouble |
L fully-qualified-class; | fully-qualified-class | |
[ type | type[] | |
(arg-types) ret-type | method type | |
V | void | void |
2)数组则以"["开始,用两个字符表示
[I | jintArray | int[] |
[F | jfloatArray | float[] |
[B | jbyteArray | byte[] |
[C | jcharArray | char[] |
[S | jshortArray | short[] |
[D | jdoubleArray | double[] |
[J | jlongArray | long[] |
[Z | jbooleanArray | boolean[] |
3)类描述符:以“L”开头,以“;”结尾;中间用“/”隔开;其对应的C函数名的参数则为jobject。
eg:Ljava/net/Socket; Socket jobject
String是个例外:Ljava/lang/String; jstring String
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
10.从JNI调用实例方法命名规则: Call<Return Value Type>Method
"()V":表示void Func();
"(II)V":表示void Func(int, int);
"(Ljava/lang/String;Ljava/lang/String;)V"
eg:
jmethodID mId = (*env)->GetMethodID(env, jcls, "toLog", "()V");
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
实例:在Native层返回一个int型二维数组(inta[ ][ ])
public class HelloJni {
...
//参数代表几行几列数组 ,形式如:int a[dimon][dimon]
private native int[][] getTwoArray(int dimon) ;
...
}
Native层该方法实现为 :
//返回int二维数组
JNIEXPORT jintArray JNICALL Java_com_sxy_JniTest_HelloWorld_getIntArray(JNIEnv *env, jobject obj, jint num)
{
//得到类名
// jclass jcls = (*env)->GetObjectClass(env, obj);
//获得一维数组 的类引用,即jintArray类型
jclass intArrCls = (*env)->FindClass(env, "[I");
if(intArrCls == NULL)
{
return NULL;
}
//构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
jobjectArray result = (*env)->NewObjectArray(env, num, intArrCls, NULL);
if(result == NULL){
return NULL;
}
int i=0;
//构建num个一维数组,并且将其引用赋值给obejctIntArray对象数组
for(i=0; i<num; i++)
{
//初始化一个容器,假设 num<256 ;
jint temp[256];
int j;
//构建jint型一维数组
jintArray iarr = (*env)->NewIntArray(env, num);
if(iarr == NULL)
{
return NULL;
}
for(j=0; j<num; j++)
{
temp[j] = i+j;
}
//设置jint型一维数组的值
(*env)->SetIntArrayRegion(env, iarr, 0, num, temp);
//给object对象数组赋值,即保持对jint一维数组的引用
(*env)->SetObjectArrayElement(env, result, i, iarr);
//删除局部引用
(*env)->DeleteLocalRef(env, iarr);
}
//返回该对象数组
return result;
}
获取类型变量名:
env->GetFieldID(cls, "name", "Ljava/lang/String;");//获取String型变量名ID
env->GetFieldID(cls, "nameid", "I");//获取int型变量名ID
env->GetFieldID(cls, "nameidy", "Z");//获取boolean型变量名ID
jclass intArrCls = env->FindClass("[I");//获取int数组
------------------------------------------------
Android.mk是Android提供的一种makefile文件,用来指定诸如编译生成so库名、引用的头文件目录、需要编译的.c/.cpp文件和.a静态库文件等。要掌握jni,就必须熟练掌握Android.mk的语法规范。
一、Android.mk文件的用途
一个android子项目中会存在一个或多个Android.mk文件
1、单一的Android.mk文件
直接参考NDK的sample目录下的hello-jni项目,在这个项目中只有一个Android.mk文件
2、多个Android.mk文件
如果需要编译的模块比较多,我们可能会将对应的模块放置在相应的目录中,
这样,我们可以在每个目录中定义对应的Android.mk文件(类似于上面的写法),
最后,在根目录放置一个Android.mk文件,内容如下:
include $(call all-subdir-makefiles)
只需要这一行就可以了,它的作用就是包含所有子目录中的Android.mk文件
3、多个模块共用一个Android.mk
这个文件允许你将源文件组织成模块,这个模块中含有:
-静态库(.a文件)
-动态库(.so文件)
只有共享库才能被安装/复制到您的应用软件(APK)包中
include $(BUILD_STATIC_LIBRARY),编译出的是静态库
include $(BUILD_SHARED_LIBRARY),编译出的是动态库