原文转载于: http://www.cnblogs.com/zhangweia/archive/2010/09/16/1828176.html
NDK动态库的调用分两种情况。第一种是:so库和调用程序在同一个目录和源程序 通过同一个mk文件来编译;另外一种情况是so是外部已经编译好了的,调用程序加载调用。下面我们就来分析下面2种so的调用情况:
第一部分: 内部so调用
A. 先看下工程的目录:
test01.java :Java文件
test01.c 主so,生成libtest.so,在libtest.so中调用libtutorial.so中的方法
tutorial01.c tutorial02.c tutorial02.h :生成libtutorial.so
B. 然后看下具体的mk文件的写法以及java中的文件内容
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.test; import android.R.integer; import android.app.Activity; import android.widget.TextView; import android.os.Bundle; public class test01 extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* Create a TextView and set its content. * the text is retrieved by calling a native * function. */ TextView tv = new TextView(this); int a = getinformation(); String lls = a +""; tv.setText(lls); setContentView(tv); } /* A native method that is implemented by the * 'hello-jni' native library, which is packaged * with this application. */ public native int getinformation(); /* this is used to load the 'hello-jni' library on application * startup. The library has already been unpacked into * /data/data/com.example.HelloJni/lib/libhello-jni.so at * installation time by the package manager. */ static { System.loadLibrary("test"); } }
LOCAL_PATH := $(call my-dir) #获取当前目录 include $(CLEAR_VARS) #清除一些变量 LOCAL_MODULE := tutorial #要生成的库名 LOCAL_SRC_FILES := tutorial01.c tutorial02.c #库对应的源文件 include $(BUILD_SHARED_LIBRARY) #生成动态库libtutorial.so include $(CLEAR_VARS) #清除一些变量 LOCAL_MODULE := test #定义另外一个库的名 LOCAL_SRC_FILES := test01.c #定义库对应的源文件 LOCAL_LDLIBS := -ldl -llog #libtest.so需要引用的库libdl.so:加载动态函数需要,liblog.so 日志打印需要,默认是system/lib目录下 include $(BUILD_SHARED_LIBRARY) #生成共享库
#include <string.h> #include <jni.h> #include <dlfcn.h> #include <android/log.h> #include <stdlib.h> #define LOG_TAG "libgl2jni" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) //extern int getinformation(); jint Java_com_example_test_test01_getinformation(JNIEnv* env,jobject thiz) { //getinformation(); ??thr .so file will load to the sdcard with the folder data/data/com.example.test/lib/ void* filehandle = dlopen("/data/data/com.example.test/lib/libtutorial.so", RTLD_LAZY ); int ll = -1; if(filehandle) { LOGI("open so success!"); int( * getinformation ) (); getinformation = dlsym(filehandle, "getinformation"); if( getinformation ) { LOGI("call function getinformation OK!"); ll = getinformation(); } else { ll = -3; LOGE("call function getinformation! ERROR!"); } LOGI("return value=%d",ll); dlclose(filehandle); filehandle=0; } else { ll = -2; LOGE("open so ERROR!"); } return ll; }
#include <stdio.h> #include "tutorial02.h" int getinformation() { int c = getinformation2(191,81); return c; }
#include <stdio.h> #include "tutorial02.h" int getinformation2(int i,int j) { return (i+j); }
int getinformation2(int,int);
C: 编译:
进入工程目录,执行 $NDK/ndk-build
输出结果:
/cygdrive/e/cygwin/android-ndk-r4 Compile thumb : test <= /cygdrive/e/cygwin/android-ndk-r4/samples/testappso/jni/test01.c SharedLibrary : libtest.so Install : libtest.so => /cygdrive/e/cygwin/android-ndk-r4/samples/testappso/libs/armeabi Compile thumb : tutorial <= /cygdrive/e/cygwin/android-ndk-r4/samples/testappso/jni/tutorial01.c Compile thumb : tutorial <= /cygdrive/e/cygwin/android-ndk-r4/samples/testappso/jni/tutorial02.c SharedLibrary : libtutorial.so Install : libtutorial.so => /cygdrive/e/cygwin/android-ndk-r4/samples/testappso/libs/armeabi
D:在重新刷新工程,你就可以在lib下面看到生成的so了,点击加载运行
第二部分:调用通过自己编写make方式生成的so
我们还是引用上面的工程,不过把libtutorial.so 拿出来,通过自己写makefile来编译成so,这里请大家注意了,调用外部的so时候,不能够直接用在linux下生产的so来调用,必须通过ndk提供的gcc来编译,否则会因为平台的不同无法调用。
A. 废话不多说,看makefile怎么写!我的ndk是1.6版本的.
新建一个文件夹,把 tutorial01.c tutorial02.c tutorial02.h 文件拷贝过去,然后编写makefile文件
CC = /cygdrive/e/cygwin/android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0/bin/arm-eabi-gcc-4.4.0 CFLAGS = -g -O2 -fPIC -I/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include SDFLAGS = -nostdlib -Wl,-T,armelf.xsc -Wl,-shared,-Bsymbolic -Wl,-soname,$@ -lc CRT_OBJS= -L/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/lib -lz -lm -ldl # source files: SRCS= tutorial01.c tutorial02.c tutorial02.h all: libtutorial.so libtutorial.so: tutorial01.o tutorial02.o $(CC) $(SDFLAGS) -o $@ tutorial01.o tutorial02.o $(CRT_OBJS) tutorial01.o: tutorial02.h tutorial02.o: tutorial02.h clean: rm -f libtutorial.so *.o
在执行make的时候需要注意一下几点:
1. 指定程序需要的头文件目录 通过-I指定/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include NDK提供的一些基本c语言库的头文件
2. 指定库路径-L/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/lib 里面有libz.so,libm.so,libdl.so等库的路径
3. 拷贝交叉编译文件armelf.xsc到android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0下,armelf.xsc在目录./build/prebuilt/linux-x86/arm-eabi-4.4.0/arm-eabi/lib/ldscripts下
4. 拷贝libc.so到./build/prebuilt/linux-x86/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0目录下,libc.so位于./build/platforms/android-5/arch-arm/usr/lib库
完成上面的工作你就可以放心make了,成功后生成libtutorial.so文件
CC = /cygdrive/e/cygwin/android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0/bin/arm-eabi-gcc-4.4.0 CFLAGS = -g -O2 -fPIC -I/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include SDFLAGS = -nostdlib -Wl,-T,armelf.xsc -Wl,-shared,-Bsymbolic -Wl,-soname,$@ -lc CRT_OBJS= -L/cygdrive/e/cygwin/android-ndk-r4/build/platforms/android-5/arch-arm/usr/lib -lz -lm -ldl # source files: SRCS= test01.c all: libtest.so libtest.so: test01.o $(CC) $(SDFLAGS) -o $@ test01.o $(CRT_OBJS) test01.o: test01.c clean: rm -f libtest.so *.o
B. 生成so文件后,在工程目录下建立目录libs/armeabi,然后把so拷贝进去,在打开工程,刷新加载,一切搞定
C. 一些小技巧:
a. 查看so是不是编译成ARM模式下的so
$ file libtest.so
libtest.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped
b. 如果别人提供了你一个so,查看提供了那些方法,更详细的用法,查看nm命令
$ nm libtutorial.so |grep T
00001344 a _GLOBAL_OFFSET_TABLE_
000002a8 T getinformation
000002b4 T getinformation2
D. 下面提供一个大工程的编译的makefile,吧opencore --omx下的testapp独立编译成so,编译通过,未加载库,给大家参考。
cc=/home/zhangweia/android/android-ndk-r4b/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-gcc-4.4.0 CFLAGS = -g -O2 -fPIC -DANDROID -I./include -I/home/zhangweia/android/android-ndk-r4b/build/platforms/android-5/arch-arm/usr/include CFLAGS += -I$(SDK)/out/target/product/generic/obj/include/libpv \ -I$(opencore)/codecs_v2/common/include \ -I$(opencore)/pvmi/pvmf/include \ -I$(opencore)/nodes/common/include \ -I$(opencore)/extern_libs_v2/khronos/openmax/include \ -I$(opencore)/build_config/opencore_dynamic \ -I$(opencore)/android/drm/oma1/src \ -I$(opencore)/engines/author/include \ -I$(opencore)/oscl/oscl/config/shared \ -I$(opencore)/oscl/oscl/config/android \ -I$(opencore)/engines/common/include \ -I$(opencore)/extern_libs_v2/khronos/openmax/include \ -I$(opencore)/android \ #SDFLAGS = -nostdlib -Wl,-T,armelf.xsc -Wl,-shared,-Bsymbolic -Wl,-soname,$@ -lc SDFLAGS = -nostdlib -Wl,-T,armelf.xsc -Wl,-Bsymbolic -Wl,-soname,$@ -lc CRT_OBJS=-L/home/zhangweia/android/android-ndk-r4b/build/platforms/android-5/arch-arm/usr/lib -lz -lm -ldl #all : libomxde.so OBJECT= omx_threadsafe_callbacks.o \ omxdectest.o \ omxdectestbase.o \ omxtest_buffer_busy.o \ omxtest_corrupt_nal.o \ omxtest_dynamic_reconfig.o \ omxtest_eos_missing.o \ omxtest_extra_partialframes.o \ omxtest_flush_eos.o \ omxtest_flush_port.o \ omxtest_get_role.o \ omxtest_incomplete_nal.o \ omxtest_missing_nal.o \ omxtest_multiple_instance.o \ omxtest_param_negotiation.o \ omxtest_partialframes.o \ omxtest_pause_resume.o \ omxtest_portreconfig_transit_1.o \ omxtest_portreconfig_transit_2.o \ omxtest_portreconfig_transit_3.o \ omxtest_reposition.o \ omxtest_usebuffer.o \ omxtest_without_marker.o #libomxde.so : $(OBJECT) omxde : $(OBJECT) $(cc) $(SDFLAGS) -o $@ $(CRT_OBJS) omx_threadsafe_callbacks.o:src/omx_threadsafe_callbacks.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omx_threadsafe_callbacks.cpp omxdectest.o:src/omxdectest.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxdectest.cpp omxdectestbase.o:src/omxdectestbase.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxdectestbase.cpp omxtest_corrupt_nal.o:src/omxtest_corrupt_nal.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_corrupt_nal.cpp omxtest_buffer_busy.o:src/omxtest_buffer_busy.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_buffer_busy.cpp omxtest_dynamic_reconfig.o:src/omxtest_dynamic_reconfig.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_dynamic_reconfig.cpp omxtest_eos_missing.o:src/omxtest_eos_missing.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_eos_missing.cpp omxtest_extra_partialframes.o:src/omxtest_extra_partialframes.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_extra_partialframes.cpp omxtest_flush_eos.o:src/omxtest_flush_eos.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_flush_eos.cpp omxtest_flush_port.o:src/omxtest_flush_port.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_flush_port.cpp omxtest_get_role.o:src/omxtest_get_role.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_get_role.cpp omxtest_incomplete_nal.o:src/omxtest_incomplete_nal.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_incomplete_nal.cpp omxtest_missing_nal.o:src/omxtest_missing_nal.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_missing_nal.cpp omxtest_multiple_instance.o:src/omxtest_multiple_instance.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_multiple_instance.cpp omxtest_param_negotiation.o:src/omxtest_param_negotiation.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_param_negotiation.cpp omxtest_partialframes.o:src/omxtest_partialframes.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_partialframes.cpp omxtest_pause_resume.o:src/omxtest_pause_resume.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_pause_resume.cpp omxtest_portreconfig_transit_1.o:src/omxtest_portreconfig_transit_1.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_portreconfig_transit_1.cpp omxtest_portreconfig_transit_2.o:src/omxtest_portreconfig_transit_2.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_portreconfig_transit_1.cpp omxtest_portreconfig_transit_3.o:src/omxtest_portreconfig_transit_3.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_portreconfig_transit_1.cpp omxtest_reposition.o:src/omxtest_reposition.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_reposition.cpp omxtest_usebuffer.o:src/omxtest_usebuffer.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_usebuffer.cpp omxtest_without_marker.o:src/omxtest_without_marker.cpp $(cc) $(CFLAGS) $(SDFLAGS) -c src/omxtest_without_marker.cpp clean: rm -rf *.o