首页 > 代码库 > JNI开发步骤注意总结:

JNI开发步骤注意总结:

开发工具和环境搭建:
第一个工具:
NDK (native develop kits)  
交叉编译的工具链:
交叉编译: 在一个平台(处理器,操作系统)下 编译出来另外一个平台下可以运行的代码.
windows AMD intel x86 架构-> 手机 android arm处理器
.c .java 
1. 编译  把源代码(高级语言)编译成一个低级语言 (汇编语言)
2. 连接  根据具体平台的特性,(cpu的类型 x86 arm,操作系统的类型) 
         连接成一个可以执行的二进制可执行的程序.
第二个工具: 
cygwin: windows下linux环境的模拟器.
cygwin的安装目录 不能有空格,最好不要中文 
官方下载地址:https://cygwin.com/packages/ 
一般选择本地按照好,速度快;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(thisnew String(hell_1from_1c().getBytes("iso-8859-1"),"utf-8"), Toast.LENGTH_SHORT).show();
                }
                catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }  
2:在C中的乱码;
(1):
(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里面的函数和方法 

常用的开发步骤:
1.创建一个android工程( 一般会选择系统Demo修改的快一点)
 
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代码
执行完6的ndk-build一定要记得刷新F5项目后,在clean下项目运行;(针对于一些小的c语言项目库)

8.调试程序;
log.h位置:

log.so位置


在c代码中使用logcat           
(1):Android.mk文件增加LOCAL_LDLIBS += -llog
(2):C代码中增加
#include <android/log.h>
#define LOG_TAG "log.samy.from.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
 LOGI("info\n");
 LOGD("debug\n");

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); }
     */
}  

charJstring2CStr(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;
}  



项目中常用的配置信息 :
1:mutual_callback.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <jni.h>
#include <com_zsy_jni_mutual_callback_CallbackProvider.h>
#include <android/log.h>
#define LOG_TAG "log.samy.from.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)  

LOGD("new cstr=%s", cstr); 

2:Application.mk
#APP_ABI := armeabi armeabi-v7a x86
#APP_ABI := all
#APP_PLATFORM := android-10 

3: ***;(待续)











来自为知笔记(Wiz)


JNI开发步骤注意总结: