开发工具和环境搭建:
第一个工具:
NDK (native develop kits)
交叉编译的工具链:
交叉编译: 在一个平台(处理器,操作系统)下 编译出来另外一个平台下可以运行的代码.
windows AMD intel x86 架构-> 手机 android arm处理器
.c .java
1. 编译 把源代码(高级语言)编译成一个低级语言 (汇编语言)
2. 连接 根据具体平台的特性,(cpu的类型 x86 arm,操作系统的类型)
连接成一个可以执行的二进制可执行的程序.
第二个工具:
cygwin: windows下linux环境的模拟器.
cygwin的安装目录 不能有空格,最好不要中文
一般选择本地按照好,速度快;JNI开发只要选择以下两项类库就可以啦;
检查是否安装成功:点击运行图标;输入make -version(make -v)
显示出来 GNU Make 的版本 ,说明我们的cygwin模拟的linux编译环境 模拟成功了.
配置NDK:
方法一:
这种配置后,直接进入NDK路径的快捷方式:cd $NDK;
要进入NDK路径后才能运行编译路径;
ThinkPad@shengyouzhang ~
$ $NDK/ndk-build ndk-build clean;
方法二:
默认的情况Cygwin模式下的DOS没有快速编辑模式,得自己去设置;设置后可以随时粘贴复制快捷用;
(Window环境下执行编译 在DOS下运行:D:\samy\cygwin\android-ndk-r8b\ndk-build.cmd )
这里的设置最好通过复制路径上去,以免出错;
配置完成后,输入 ndk-build;ps: cd $NDK(可快捷进入NDK所在路径);
第三个工具:
cdt : c/c++ develop tools (主要是用来让c和c++代码 完成高亮显示的作用)
adt : android develop tools
环境变量的作用: 方便的在任何目录下 都可以使用 指定目录里面的工具
LOCAL_PATH := $(call my-dir)
# 交叉编译器 在编译c代码/c++代码依赖的配置文件 linux下 makefile的语法的子集
# 获取当前Android.mk的路径
#变量的初始化操作 特点: 不会重新初始化LOCAL_PATH 的变量
include $(CLEAR_VARS)
#libHello.so 加lib前缀 .so后缀 makefile的语法约定的
LOCAL_MODULE := libHello
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY)
ndk开发常见错误
1. android.mk文件不存在
$ ndk-build
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk
/cygdrive/h/heima6/jni2/ziliao/android-ndk-r7b/build/core/add-application.mk:133: *** Android NDK: Aborting... 。 停止。
2.android.mk文件 的配置信息有错误
$ ndk-build
/cygdrive/h/heima6/jni2/ziliao/android-ndk-r7b/build/core/build-shared-library.mk:23:
*** Android NDK: Missing LOCAL_MODULE before including BUILD_SHARED_LIBRARY in jni/Android.mk 。 停止。
3.c代码 语法出现错误,编译不通过 Error 1.
Compile thumb : Hello <= Hello.c
jni/Hello.c: In function ‘Java_cn_itcast_ndk_DemoActivity_helloFromC‘:
jni/Hello.c:21: error: ‘ctr‘ undeclared (first use in this function)
jni/Hello.c:21: error: (Each undeclared identifier is reported only once
jni/Hello.c:21: error: for each function it appears in.)
jni/Hello.c:21: error: expected ‘;‘ before ‘c‘
/cygdrive/h/heima6/jni2/ziliao/android-ndk-r7b/build/core/build-binary.mk:240: recipe for target `obj/local/armeabi/objs/Hello/Hello.o‘ failed
make: *** [obj/local/armeabi/objs/Hello/Hello.o] Error 1
4.java层c代码库没有找到
Caused by: java.lang.UnsatisfiedLinkError: Library Hell0 not found
静态加载代码库的时候 代码库没有找到.
5.c代码函数签名出现错误
Caused by: java.lang.UnsatisfiedLinkError: hello_from_c
6.逻辑性的错误, 使用了已经回收的内存空间, 访问了不可以被访问的内存空间,
修改了不能被修改的内存空间
7:时刻看ndk-build信息;
如下:其实是警告问题,不会影响总体效果功能;但是为了性能问题还是修改为好;
8:对应先写个方法,再次编写一个头文件; 再然后编写的和上面的方法重构的一个方法,再次编译一个头文件;此时第一个方法的第一个方法的头文件名可能也会会发生变化;故重新编写头文件的话得检查下头文件;
JNI调试方法:
1:断点---单步;
2: ndk-gdb
3:通过log 方式来观察程序执行流程
ndk开发的中文乱码问题:
1:从C到Java传递的中文;
(1):设置当前项目的格式的utf-8得跟你当前配置的C语言的格式得一样;
(2):低版本ndk 不支持中文 ndk-r4-crystal iso-8859-1 转码 ;(发现没什么效果)
try {
Toast.makeText(this, new String(hell_1from_1c().getBytes("iso-8859-1"),"utf-8"), Toast.LENGTH_SHORT).show();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
2:在C中的乱码;
(2):
实战操作:
阿里旺旺 :
登陆的具体实现 c/c++
socket http
int login(char* username,char * password){
//开启一个socket 连接服务器 , 把username 和 password传给服务器.
//服务器返回 200 登陆成功
//服务器返回404 登陆失败
}
public native int Login(String username, String pwd);
javah 生成native方法的签名
jint Java_xxx_xxx_xxx_xxx_Login(JNIENV* env , jobject obj , jstring username, jstring pwd){
char* cusername = JStr2Cstr();
char* cpwd = JStr2Cstr();
int result = login(cusername ,cpwd);
if(result == 200){
}
}
kiss : keep it simaple stupid
int String byte[]
int[]
1,2,3,4,5
c代码写文件
java读文件
java jni 粘合剂 胶水
c工程实现 具体的代码
jni native ->jni ->.c
1. 当c代码接受到特定的消息 让c代码通知 java代码
2. c程序员 c代码想复用java里面的函数和方法
2.JAVA代码中写声明native 方法 public native String helloFromC();
配置好Android.mk,ps:编写拷贝时一定得注意空格;即可;
3.用javah工具生成头文件
如果是简单的函数名,可以直接自己编写。
如:Java_com_zsy_jni_test_JniTestAct_hellFromC;
Java_com_zsy_jni_test_JniTestAct_hell_1from_1c ;
如果是复杂的函数包名建议用Javah工具自动生成;
ps:(1):jni 头文件编译C语言时;跟jdk的版本有很大关系;java -version;1.7版本的编译要配置其他jar包如rt.jar;
(2):(记住得编译下项目,才能同步本地方法到.class中去)把路径切换到.bin/classes下运行:
samy@shengyouzhang /cygdrive/d/samy/workspace/samy_09_25/jni_test/bin/classes
$ javah com.zsy.jni.test.JniTestAct
jstring Java_com_zsy_jni_test_JniTestAct_hell_11from_11c(JNIEnv* env,
jobject obj) {} JNIEnv 类型代表了java环境 通过JNIEnv* 指针,就可以对java端的代码进行操作.
创建java类的对象,调用java对象的方法
获取java对象的属性 等等.
jobject是个什么
jobject obj 就是当前方法所在的类代表的对象.
4. 创建jni目录,引入头文件,根据头文件实现c代码
在实际开发中会开两个窗口切换着用;
5.编写***.mk文件
(1):编写Android.mk文件
参照:位置:D:\samy\cygwin\android-ndk-r8b\docs\ANDROID-MK.html
Android.mk 的含义
LOCAL_PATH:=$(call my-dir)LOCAL_PATH是定义源文件在哪个目录用的.
my-dir 是个定义的宏方法, $(call my-dir)就是调用这个叫 my-dir的宏方法,这个方法返回值就是Android.mk文件所在的目录
include $(CLEAR_VARS)CLEAR_BARS 变量是build system里面的一个变量;这个变量指向了所有的类似 LOCAL_XXX的变量,
执行完这一句话, 这个编译系统就把 所有的类似
LOCAL_MODULE,_SRC_FILELOCALS,LOCAL_STATIC_LIBRARIES,...这样的变量都清除掉
但是不会清除掉 LOCAL_PATH
LOCAL_MODULE 就是你要生成的库的名字,名字要是唯一的这个.不能有空格.
编译后系统会自动在前面加上lib的头, 比如说我们的Hello 就编译成了libHello.so
还有个特点就是如果你起名叫libHello 编译后ndk就不会给你的module名字前加上lib了
但是你最后调用的时候 还是调用Hello这个库
LOCAL_SRC_FILES = :Hello.c
这个是指定你要编译哪些文件
不需要指定头文件 ,引用哪些依赖, 因为编译器会自动找到这些依赖 自动编译
include $(BUILD_SHARED_LIBRARY)
BUILD_STATIC_LIBRARY.so
编译后生成的库的类型,如果是静态库.a 配置include $(BUILD_STATIC_LIBRARY)
别的参数
LOCAL_CPP_EXTENSION := cc //指定c++文件的扩展名和C语言的配置区别;
LOCAL_MODULE := ndkfoo
LOCAL_SRC_FILES := ndkfoo.cc
LOCAL_LDLIBS += -llog -lvmsagent -lmpnet -lmpxml -lH264Android
//指定需要加载一些别的什么库.
总体配置如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello
LOCAL_SRC_FILES := Hello.c
#增加log函数对应的log库
#引入函数库方法 : 使用 LOCAL_LDLIBS += -l函数库名, 注意函数库名不带lib前缀 和.so 后缀, 同时可以添加多个库, 使用 -l库1 -l库2 -库3 ;
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
(2):Application.mk 编写:(一般会关掉这部,方便调试打印用,要不然打印很多多东西,不利于查看,到最后调试好加上配置各个平台适配,不同配置还是有很大区别的如性能和加载方式,用错了平台可能会很难找到的Bug;)
配置各个平台的设置(ps:这个配置跟你NDK的D:\samy\android-ndk-r9d_x86\platforms有关系)
#APP_ABI := armeabi armeabi-v7a x86
APP_ABI := all; APP_PLATFORM := android-9;//的版本号必须跟AndroidManifest.xml中的 android:minSdkVersion="9"保持一致;
6.Ndk编译生成动态库
命令:ndk-build ndk-build clean;
7.Java代码load 动态库.调用native代码
C++简单案例开发:
LOCAL_CPP_EXTENSION := cc //指定c++文件的扩展名和C语言的配置区别;
ps:发现可以有.cc和.cpp格式的C++语言后缀;其他好像不可以;
LOCAL_MODULE := ndkfoo
LOCAL_SRC_FILES := ndkfoo.cc
LOCAL_LDLIBS += -llog -lvmsagent -lmpnet -lmpxml -lH264Android
//指定需要加载一些别的什么库.
和C语言的比较:(总的来说C++是在C语言的基础上再包了一层,所以写起来相对来说更简单点)
JNIEXPORT jstring JNICALL Java_com_zsy_jni_cpp_test_JniTestAct_hellFromCPP(JNIEnv * env, jobject obj) {
// return (*env)->NewStringUTF(env,"haha from c");
return env->NewStringUTF("haha from cpp end with cc by samy");
/*
* jstring NewStringUTF(const char* bytes)
{ return functions->NewStringUTF(this, bytes); }
*/
}
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;
} 原文地址:http://www.cnblogs.com/hongfeiliuxing/p/446738066066bed92721635e0cbfe424.html