char* Jstring2CStr(JNIEnv* env, jstring jstr){
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"GB2312");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1); //"\0"
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
(*env)->ReleaseByteArrayElements(env,barr,ba,0); //
return rtn;
}
NDK工具是提供给Linux系统用的(随着版本的升级也可以直接在Windows下使用,但是现在仍不完善有bug), 所以要在windows下使用ndk的工具,必须要提供一个工具(linux环境的模拟器)
linux 特点:所有的设备 硬件 都是以文件的方式定义的.
安装完后进入cygwin
打印make -v
命令如果能打印出GNU Make ...
就说明安装木问题了。
PATH="/usr/local/bin:/usr/bin:/cygdrive/d/android-ndk-r7b:${PATH}"
在这个后面加上:ndk-build的路径(注意:在linux中路径的分隔符不是分号而是冒号),
改成这样
PATH="/usr/local/bin:/usr/bin:${PATH}:/cygdrive/d/android-ndk-r7b"
//注意这里的路径是在linux系统下的ndk路径而不是windows下的路径,/cygdrive/d/是在linux下看到的d盘。###JNI开发步骤:
###JNI开发之Java中调用C代码步骤
public native String helloFromC();
C:\Users\Administrator>javah -help
用法:
javah [options] <classes>
其中, [options] 包括:
-o <file> 输出文件 (只能使用 -d 或 -o 之一)
-d <dir> 输出目录
-v -verbose 启用详细输出
-h --help -? 输出此消息
-version 输出版本信息
-jni 生成 JNI 样式的标头文件 (默认值)
-force 始终写入输出文件
-classpath <path> 从中加载类的路径
-cp <path> 从中加载类的路径
-bootclasspath <path> 从中加载引导类的路径
```
`#include <stdio.h>`
`#include <jni.h>`
//这个方法的名字的写法固定Java_表示这个方法由Java调用,cn_itcast_ndk表示java的包名DemoActivity表示
//java中调用这个方法的类名helloFromC表示java中调用这个方法的方法名字
```java
jstring Java_cn_itcast_ndk_DemoActivity_helloFromC (JNIEnv* env , jobject obj){//这两个参数是固定必不可少的
//return (*(*env)).NewStringUTF(env,"hello from c!");//调用NewStringUTF这个方法new出来一个java中的String类型的字符串
return (*env)->NewStringUTF(env,"hello from c!" );//这个写法和上面这个注释掉的一样,只是更简洁一点
}
```
3. 在jni文件夹中编写android.mk文件,在这个文件夹中声明要编译的c文件名以后编译后生成的文件名
```c
LOCAL_PATH := $(call my-dir) //将jni所在的目录返回去到LOCAL_PATH
#clear_vars 一个函数 初始化编译工具链的所有的变量.
#特点:清空所有的以LOCAL_开头的变量,但是不会清空LOCAL_PATH的变量
include $(CLEAR_VARS)
#指定编译后的文件的名称 符合linux系统下makefile的语法.
LOCAL_MODULE := Hello
#指定编译的源文件的名称 ,编译器非常智能
LOCAL_SRC_FILES := Hello.c
#指定编译后的文件的类型. 默认编译成动态库 BUILD_SHARED_LIBRARY 扩展名.so .so代码体积很小
# 静态库 BUILD_STATIC_LIBRARY 扩展名.a .a代码体积很大
include $(BUILD_SHARED_LIBRARY)
public class DemoActivity extends Activity {
//1.定义一个native的方法
public native String helloFromC();
static{
//5.把要调用的c代码 给加载到java虚拟机里面
System. loadLibrary("Hello");//注意写的是Hello不要加后缀
}
}
#include <stdio.h>//这个<>是引入工具的h文件
#include "cn_itcast_ndk2_DemoActivity.h" //对于自己工程中的h文件用""来引入或者引入#include <jni.h>也可以
JNIEXPORT jstring JNICALL Java_cn_itcast_ndk2_DemoActivity_hello_1_1_1from_1_1_1c //拷贝h文件中生成的方法名
(JNIEnv * env, jobject obj){
return (*env)->NewStringUTF(env,"hello_from_c 2!" );
}
然后就和上面的步骤一样了 注意上面的这个javah的用法师在jdk1.6中用的,如果在jdk1.7中就不能这样用了 对于jdk1.7在使用javah的工具的时候就不能够直接进入到classes目录下直接运行命令了, 而是要将sdk中的platforms下的android版本中的android.jar这个路径加载到classPath的环境变量中(麻烦),或者是直接进入到src目录下用javah包名.类名(简单常用)
#include <android/log.h> //导入log.h
#define LOG_TAG "clog" //指定打印到logcat的Tag
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) //对后面的这个打印日志的方法起一个别名是LOGD
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Hello
LOCAL_SRC_FILES := Hello.c
LOCAL_LDLIBS += -llog //新增加这一句,作用是 #把c语言调用的log函数对应的函数库加入到编译的运行时里面 #liblog.so,如果还要加载其他的就在后面继续 -lXXX
include $(BUILD_SHARED_LIBRARY)
3. 在c的代码中直接使用LOGD或者LOGI就能向logcat中输入打印信息
JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add
(JNIEnv * env, jobject obj, jint x, jint y){
LOGI( "x=%d",x);
LOGD( "y=%d",y);
int result = x+y;
LOGD( "result=%d",result);
return result;
}
jstring (*NewStringUTF)(JNIEnv*, const char*);
C调用java的 思想类似于java中的反射,我们在c中就是通过反射的c实现来找到java中的这个方法, 在getMethodID的第二个参数是一个方法的签名,这里我们可以通过jdk提供的一个工具javap,来到classes目录下, 然后用 javap -s 类名.方法名 来得到一个方法的签名,这样就能列出来所有方法的签名
/**
* env JNIEnv* java虚拟机环境的指针.
*
*jobject obj ,哪个对象调用的这个native的方法 , obj就代表的是哪个对象
*/
JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callmethod1
(JNIEnv * env, jobject obj){
//思考 java中的反射
//1.找到某一个类的字节码
// jclass (*FindClass)(JNIEnv*, const char*);
jclass jclazz = (*env)->FindClass(env,"cn/itcast/ndk4/DataProvider" );
if(jclazz==0){
LOGI( "LOAD CLAZZ ERROR");
} else{
LOGI( "LOAD CLAZZ success" );
}
//2.找到类的字节码里面的方法.
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodid = (*env)->GetMethodID(env,jclazz,"helloFromJava", "()V"); //最后一个参数是方法的签名
if(methodid==0){
LOGI( "LOAD methodid ERROR" );
} else{
LOGI( "LOAD methodid success" );
}
//3.调用方法
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env,obj,methodid);
}
//将java字符串转成C++字符串的工具方法
char* Jstring2CStr(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = (env)->FindClass("java/lang/String");
jstring strencode = (env)->NewStringUTF("GB2312");
jmethodID mid = (env)->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(env)->CallObjectMethod(jstr,mid,strencode); // String .getByte("GB2312");
jsize alen = (env)->GetArrayLength(barr);
jbyte* ba = (env)->GetByteArrayElements(barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1); //"\0"
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
(env)->ReleaseByteArrayElements(barr,ba,0); //
return rtn;
}
JNIEXPORT jstring JNICALL Java_cn_itcast_cpp_DemoActivity_HelloFromC
(JNIEnv * env, jobject obj){
//C代码
//return (*env)->NewStringUTF(env,"haha from c"); 在C中env代表的是C中结构体的指针的指针
//c++代码
return env->NewStringUTF("haha from cpp");//在C++中env代表的是C++中结构体的指针
}
文件的格式: 文件的存储方式是二进制0101这样 那么怎么设别文件的类型呢?
#include<stdio.h>
main(){
//用 法: FILE *fopen(char *filename, char *type); //第二个参数是打开的方式 rt就是读文件, rb就是读二进制
FILE* fp = fopen("1.txt","rt");
//用 法: int fread (void *ptr, int size, int nitems, FILE *stream);
//ptr要读的数据 放在哪一块内存空间里面.
//size 一次读的数据的长度.
//nitems 读多少次
//stream 从哪个文件里面 读
char* buffer = malloc(sizeof(char)*12);
int len= fread(buffer,sizeof(char),12,fp);
printf("读了%d个char\n",len);
printf("str=%s\n",buffer);
fclose(fp); //关闭掉流
system("pause");
}