记录生成.so库的步骤,以防自己每次忘记。用的是eclipse。
一、先在eclipse中生成一个android工程,然后在android工程下的src新建一个package,例如名字为com.android.aa,在其下新建一个java文件,这个文件相当于与.so库的一个对接接口,其形式为:
package com.android.aa;
public class NativeAA{
static{
System.loadLibrary("aa"); //加载aa.so库
}
public native byte[] sent(String input); //调用so库中的sent函数
}
保存一下,就可以在./bin/classes/com/android/aa文件下生成NativeAA.class文件。
首先利用cmd进入目录中:例如工程放在了F盘,工程为test:
f: //这是进入了f盘
cd ./test/bin/classes目录下,运行下一命令:
javah com.android.aa.NativeAA
就会在classes生成了一个com_android_aa_NativeAA.h的头文件
里面就是一个函数的声明:
JNIEXPORT jbyteArray JNICALL Java_com_android_aa_NativeAA_sent
(JNIEnv*,jobject,jstring);(其他的省略,并且java中有几个函数声明,.h就对应生成几个,我这里是一个)
在Ubuntu中建立一个文件夹,例如:我在project的文件下建立了一个aa的文件下,然后建立一个jni文件下,将com_android_aa_NativeAA.h方进去,再新建一个对应的.cpp
,在命令窗口输入geditcom_android_aa_NativeAA.cpp,就建立以个.cpp文件。再建立实现sent函数的.cpp和.h这里命名为sent.cpp和sent.h。
sent.cpp的中的函数sent实现的是输入为char*,输出的参数为char*类型,其声明为:
int sent(char* sentInput,char **sentInput);
char*sentInput是输入,char**sentInput是输出(实际需要的是char*,返回二级指针下面会说明),返回int是标志位,这个程序运行下来返回0表示成功;其他值表示失败。
com_android_aa_NativeAA.cpp的形式:
#include "com_android_aa_NativeAA.h"
#define TAG "so-jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) //
//功能:实现将jstring转换为char*
char*jstringTostring(JNIEnv*env,jstring jstr);
JNIEXPORT jbyteArray JNICALL Java_com_android_aa_NativeAA_sent
(JNIEnv*env,jobject object,jstring jinput) //jstring jinput对应的是java文件中的String input
{
//首先将jstring转成char类型,因为我的例程中sent.cpp中的函数的输入参数是char*;
char *inputChar=jstringTostring(env,jinput);
if(inputChar==NULL)
{
LOGD("jstring转换char失败!")
return NULL;
}
else
{
char *outData=new char[255];
memset(outData,255);
if(outData==NULL)
return NULL;
int ret=sentInput(InputChar,&outData); //outData是返回值,&outData这样做是返回一组数组。
if(ret==0)
{
//这是将c++中的char*转为为java中的byte[];
jbyte *joutData=(jbyte*)outData;
jbyteArray jarrOutData=env->NewByteArray(255);
env->SetByteArrayRegion(jarrOutData,255,joutData);
delete[ ] outData;
return jarrOutData;
}
else
{
delete[] outData;
return NULL;
}
}
delete[ ] inputData;
}
//将Java中的String转换为c++中的char*
char*jstringTostring(JNIEnv* env,jstring jstr)
{
char*ttn=NULL;
jclass clsstring=env->env->FindClass("java/lang/string");
jstring strInput=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>0)
{
rtn=(char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
env->ReleaseByteArrayElements(barr,0);
return rtn;
}
四、两个makefile文件(1)Application.mk:用于描述APP需要的native model
其内容为:
APP_PLATFORM :=android-19
APP_STL :=gnustl_static #要使用到的标准c++库
APP_CPPFLAGS :=-frtti -fexceptions
APP_ABI :=armeabin
分析:
Application.mk常用的有四个:
a. APP_PLATFORM :=android-19
表示使用ndk库函数版本号。一般和SDK的版本相对应,各个版本在NDK目录下的platforms文件夹中
b. APP_STL :=stlport_static
定义NDK的编译系统的运行时库,有如下几种:
system:默认最小的c++运行库,生成的应用体积小,内存占用少,但部分功能将无法支持
stIport_static:使用STLport作为静态库(推荐使用)
stlport_shared:STLport作为动态库(不推荐,可能产生兼容性和部分低版本的Android固件)
gnustl_static:使用GNU libstdc++作为静态库
c. APP_ABI:=armeabi
表示要编译成对应指令集cpu的动态库,armeabi:ARMv7指令集,armeabi-v7a:ARMv7+硬件FPU指令集,x86:IA-32指令集,all支持常用的指令集
d. APP_CPPFLAGS:该变量列出了一些编译器标志,在便以任何模块的c++代码时这些标志都会被传给编译器;
e. APP_OPTIM:=debug(我自己的项目没用这一项)
设置编译类型,debug:调试版本,so动态库中带调试信息(可以使用gdb-server尽心动态段断点低调试),release:发布版本,so库不带调试信息
(2)Android.mk:用于描述构建系统的源文件以及shareed libraries。
其内容:
LOCAL_PATH :=&(call my-dir)
APP_STL :=gnustl_static #要用到标准c++库,如果不用可以注释掉
include $(CLEAR_VARS)
LOCAL_LDLIBS:=-llog
LOCAL_MODULE :=aa #aa为行程so库的库名
LOCAL_SRC_FILES :=sent.cpp \
com_android_usbkey_NativeUsbBase.cpp \
LOCAL_CFLAGS :=-arm -mfloat-abi=softfp -mfpu=vfp -Wmultichar
include $(BUILD_SHARED_LIBRARY)
总结:
要行程.so库的流程就这些(可根据自己的情况添加.cpp),需注意的是:ubuntu上必须安装ndk上述文件才可以形成.so库。
在命令窗口需输入的:
1.首先进入jni文件所在的目录(删除的文件都放在jni文件里);
2.输入命令:ndk-build即可生成so库(注意:清楚so库的命令为:ndk-build clean)
原文链接:https://www.f2er.com/ubuntu/352485.html