1.动态库

目录

linux测试

1、动态链接库生产

2、获取动态链接库的函数

3.makefile管理

3.1目录结构

3.2测试

JavaJNI测试

目录结构

测试

Android_JNI


linux测试

1、动态链接库生产

  动态链接库与普通的程序相比而言,没有main函数是一系列函数的实现。通过sharedfPIC编译参数生产so动态链接库文件。程序在调用库函数时,只需要连接上这个库即可。例如下面实现一个简单的整数四则运输的动态链接库,定义的caculate.h和caculate.c两个文件,生产libcac.so动态链接库。

程序代码如下:

/*caculate.h*/

#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
int add(int a, int b);
//减法
int sub(int a, int b);
//除法
int div(int a, int b);
//乘法
int mul(int a, int b);

#endif
/*caculate.c文件*/#include "caculate.h"

//求两个数的和
int add(int a, int b)
{
    return (a + b);
}
//减法
int sub(int a, int b)
{
    return (a - b);
}
//除法
int div(int a, int b)
{
    return (int)(a / b);
}
//乘法
int mul(int a, int b)
{
    return (a * b);
}

编译生产libcac.so文件如下: gcc -shared -fPIC caculate.c -o libcac.so
编写一个测试程序调用此动态链接库的函数,程序如下所示:

#include <stdio.h>
#include "caculate.h"

int main()
{
    int a = 20;
    int b = 10;
    printf("%d + %d = %d\n", a, b, add(a, b));
    printf("%d - %d = %d\n", a, b, sub(a, b));
    printf("%d / %d = %d\n", a, b, div(a, b));
    printf("%d * %d = %d\n", a, b, mul(a, b));
    return 0;
}

编译生产可执行文件main如下:gcc main.c -o main -L ./ -lcac   (其中-L指明动态链接库的路径,-l后是链接库的名称,省略lib)

拷贝libcac.so到/usr/lib下
程序执行结果如下所示:

rpdzkj@ubuntu:~/jq_project/makefilelib_test$ ./main 
20 + 10 = 30
20 - 10 = 10
20 / 10 = 2
20 * 10 = 200

2、获取动态链接库的函数


  linux提供dlopen、dlsym、dlerror和dlcolose函数获取动态链接库的函数。通过这个四个函数可以实现一个插件程序,方便程序的扩展和维护。函数格式如下所示:

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);

char *dlerror(void);

void *dlsym(void *handle, const char *symbol);

int dlclose(void *handle);

 Link with -ldl.

dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。写个测试程序调用上面生产libcac.so库如下所示:

#include <stdio.h>
#include <dlfcn.h>

#define DLL_FILE_NAME "libcac.so"

int main()
{
    void *handle;
    int (*func)(int, int);
    char *error;
    int a = 30;
    int b = 5;

    handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
    if (handle == NULL)
    {
    fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
    return -1;
    }

    func = dlsym(handle, "add");
    printf("%d + %d = %d\n", a, b, func(a, b));

    func = dlsym(handle, "sub");
    printf("%d + %d = %d\n", a, b, func(a, b));

    func = dlsym(handle, "div");
    printf("%d + %d = %d\n", a, b, func(a, b));
    
    func = dlsym(handle, "mul");
    printf("%d + %d = %d\n", a, b, func(a, b));

    dlclose(handle);
    return 0;
}

程序执行结果如下所示:gcc call_main.c -o call_main -ldl

rpdzkj@ubuntu:~/jq_project/makefilelib_test$ ./call_main 
30 + 5 = 35
30 + 5 = 25
30 + 5 = 6
30 + 5 = 150

3.makefile管理

新建src和inc目录

src存放.c文件,inc存放.h文件,顶层目录存放Makefile文件

3.1目录结构

src/caculate.c
inc/test.h

Makefile

#定义伪目标
.PHONY : all clean copy

#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/x_ray/* 

#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o

#定义编译器变量
CC := gcc

#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=

#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)

#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif

#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out

#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libcac

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))

#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))

#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)

#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
	@echo "Target File ==> $(APP)"

$(DIR_BUILD) $(DIR_LIB):
	$(MKDIR) $@

$(TARGET).so : $(OBJS)
	@echo Generating shared lib...
	$(CC) -shared -fPIC -o  $(TARGET).so $(OBJS)

$(APP) : $(OBJS)
	$(CC) $(LFLAGS) -o $@ $^

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
	$(CC) -c -fPIC $(CFLAGS) $< -o  $@
#	$(CC) $(CFLAGS) -o $@ -c $<

clean :
	$(RM) $(DIR_BUILD)  $(DIR_LIB) *~ SI

copy :
	$(CP) $(DIR_CODE) .

3.2测试

生成的动态库libcac.so拷贝到顶层目录

编写测试程序call_main.c和对应头文件caculate.h

JavaJNI测试

新建src和inc目录

src存放.c文件,inc存放.h文件,顶层目录存放Makefile文件

目录结构

JNIDemo.java

src/caculate.c

src/native.c

inc/caculate.h

Makefile

native.c

#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include "caculate.h"
#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif

void c_hello(JNIEnv *env, jobject cls)
{
	printf("add(1, 2) = %d\n", add(1, 2));
	printf("Hello, world!\n");
}


static const JNINativeMethod methods[] = {
	{"hello", "()V", (void *)c_hello},
};



/*
1.首先实现一个JNI_OnLoad方法
只要java里面加载了这个库,就会先执行里面实现的方法
如何实现:参考jni.pdf P117
*/
/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	//JNI_VERSION_1_4:运行环境版本
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");//查找java里面的JNIDemo类
	if (cls == NULL) {
		return JNI_ERR;
	}

	/* 2. map java hello <-->c c_hello 建立联系*/
	//这个方法来自于运行环境;参考jni.pdf P115示例
	//env:环境   cls:类 methods:方法   把这个方法注册到环境里面的这个类里
	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
		return JNI_ERR;

	return JNI_VERSION_1_4;
}

caculate.c

/*caculate.c文件*/
//#include "caculate.h"

//求两个数的和
int add(int a, int b)
{
    return (a + b);
}
//减法
int sub(int a, int b)
{
    return (a - b);
}
//除法
int div(int a, int b)
{
    return (int)(a / b);
}
//乘法
int mul(int a, int b)
{
    return (a * b);
}

caculate.h

/*caculate.h*/

#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
int add(int a, int b);
//减法
int sub(int a, int b);
//除法
int div(int a, int b);
//乘法
int mul(int a, int b);

#endif

Makefile

#定义伪目标
.PHONY : all clean copy

#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/x_ray/* 

#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o

#定义编译器变量
CC := gcc

#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=

#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)
CFLAGS += -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/

#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif

#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out

#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libnative

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))

#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))

#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)

#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
	@echo "Target File ==> $(APP)"

$(DIR_BUILD) $(DIR_LIB):
	$(MKDIR) $@

$(TARGET).so : $(OBJS)
	@echo Generating shared lib...
	$(CC) -shared -fPIC -o  $(TARGET).so $(OBJS)

$(APP) : $(OBJS)
	$(CC) $(LFLAGS) -o $@ $^

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
	$(CC) -c -fPIC $(CFLAGS) $< -o  $@
#	$(CC) $(CFLAGS) -o $@ -c $<

clean :
	$(RM) $(DIR_BUILD)  $(DIR_LIB) *~ SI

copy :
	$(CP) $(DIR_CODE) .

 

测试

生成的动态库libcac.so拷贝到顶层目录

编写测试程序JNIDemo.java

public class JNIDemo {
	//加载库是放在静态代码快,因为构造对象之前执行,并且只执行一次
	static { 		/* 1. load 加载*/
		System.loadLibrary("native"); /* libnative.so */
 	}
	public native void hello(); //声明 native表示不是在java语言里面实现的,而是在本地语言实现的
	public static void main (String args[]) {
		JNIDemo d = new JNIDemo();		

		/* 2. map java hello <-->c c_hello 建立映射*/

		/* 3. call 调用*/
		d.hello();
	}
}

javac JNIDemo.java

java JNIDemo

 

Android_JNI

模拟访问硬件led

目录结构

inc/caculate.h

src/caculate.c

src/hardcontrol.c

Makefile

#定义伪目标
.PHONY : all clean copy

#export PATH=/usr/local/arm/arm-2009q3/bin:$PATH

#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/lib_bak 

#依赖库路径
EXTERN_LIB_PATH := /home/rpdzkj/Trk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/

#依赖库
EXTERN_LIBC := $(EXTERN_LIB_PATH)/libc.so
EXTERN_LIBLOG := $(EXTERN_LIB_PATH)/liblog.so

#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o

#定义编译器变量
CC := arm-none-linux-gnueabi-gcc

#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=

#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)
CFLAGS += -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
CFLAGS += -I /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include 

#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif

#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out

#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libhardcontrol

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))

#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))

#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)

#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
	@echo "Target File ==> $(APP)"

$(DIR_BUILD) $(DIR_LIB):
	$(MKDIR) $@

$(TARGET).so : $(OBJS)
	@echo Generating shared lib...
	$(CC) -shared -fPIC -o  $(TARGET).so $(OBJS) -nostdlib  $(EXTERN_LIBC) $(EXTERN_LIBLOG)

$(APP) : $(OBJS)
	$(CC) $(LFLAGS) -o $@ $^

#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
	$(CC) -c -fPIC  $< -o  $@ $(CFLAGS) 
#	$(CC) $(CFLAGS) -o $@ -c $<

clean :
	$(RM) $(DIR_BUILD)  $(DIR_LIB) *~ SI

copy :
	$(CP) $(DIR_LIB)/* $(DIR_CODE) 
/*caculate.h*/

#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
jint add(jint a, jint b);
//减法
jint sub(jint a, jint b);
/*
//除法
jint div(jint a, jint b);
//乘法
jint mul(jint a, jint b);
*/
#endif
/*caculate.c文件*/
//#include "caculate.h"
#include <jni.h>
//求两个数的和
jint add(jint a, jint b)
{

    return (a + b);
}
//减法
jint sub(jint a, jint b)
{
    return (a - b);
}

//除法
jint div(jint a, jint b)
{
    return (jint)(a / b);
}
//乘法
jint mul(jint a, jint b)
{
    return (a * b);
}

#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include "caculate.h"
#include <android/log.h>  /* liblog */

//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");

/*
arm-none-linux-gnueabi-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/  -nostdlib /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so -I /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
*/ 
#if 0
typedef struct {
    char *name;          /* JavaÀïµ÷Óõĺ¯ÊýÃû */
    char *signature;    /* JNI×Ö¶ÎÃèÊö•û, ÓÃÀŽ±íÊŸJavaÀïµ÷Óõĺ¯ÊýµÄ²ÎÊýºÍ•µ»ØÖµÀàÐÍ */
    void *fnPtr;          /* CÓïÑÔʵÏֵıŸµØº¯Êý */
} JNINativeMethod;
#endif

jint ledOpen(JNIEnv *env, jobject cls)
{
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen ...");
	return 0;
}

void ledClose(JNIEnv *env, jobject cls)
{
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose ...");
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
	jint tem = add(4, 6);
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl tem: %d, %d, %d...", which, status, tem);
	return 0;
}


static const JNINativeMethod methods[] = {
	{"ledOpen", "()I", (void *)ledOpen},
	{"ledClose", "()V", (void *)ledClose},
	{"ledCtrl", "(II)I", (void *)ledCtrl},
};




/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "com/example/hardlibrary/HardControl");
	if (cls == NULL) {
		return JNI_ERR;
	}

	/* 2. map java hello <-->c c_hello */
	if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
		return JNI_ERR;

	return JNI_VERSION_1_4;
}

将生成的库拷贝到AS的工程中,测试

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值