简介:本资源是一个基于 Android Studio 的 libredwg 库交叉编译工程,面向 Android 开发者及嵌入式 C/C++ 工程师,解决在安卓平台解析 DWG 文件的核心需求——无需从零配置 NDK 与 CMake 工具链,即可快速生成适配 arm64-v8a、armeabi-v7a、x86 和 x86_64 架构的 native 动态库。压缩包共 290 个文件,含 71 个头文件(.h)、49 个文本说明(.txt)、40 个 C 源码(.c)、20 个构建规范(.spec)及 8 个已编译 SO 库,另有 CMakeLists.txt、Android.mk、gradle 配置等关键构建脚本,完整呈现跨平台编译的目录结构与依赖组织逻辑。已有 59970 人学习下载,资源附带可直接运行的示例工程,支持一键 Build 生成目标架构库;同时提供通用化替换模板——用户只需替换 cpp 目录下的源码并修改 CMakeLists.txt,即可复用于其他 C/C++ 第三方库的 Android 交叉编译任务,显著降低 NDK 编译门槛。 搞Android开发的老哥们应该遇到过这种需求:要在手机上读取DWG图纸。DWG是AutoCAD的私有二进制格式,想在移动端处理它,最常见也最靠谱的做法就是找一个能解析DWG的C/C++库,然后用JNI调过去。libredwg就是一个非常合适的开源选择,但问题来了:它是个标准的Autotools项目,默认编出来是x86_64平台的库,而Android手机是ARM架构,所以必须走交叉编译这条路。
这篇博文以我自己的一次完整实践为主线,从工具链选型、依赖库处理、configure参数设置,到Android Studio里的CMake集成和JNI封装,把整个流程走了一遍。内容面向两类读者:一类是准备在Android项目里用libredwg的开发者,另一类是纯粹想搞明白“C库怎么交叉编译到Android”这个通用问题的朋友。无论你是哪一类,这篇文章都能让你少踩几个坑。
1. 为什么要交叉编译libredwg:方案选型与整体思路
1.1 libredwg的核心能力与移动端应用场景
libredwg是GNU旗下的一个开源C库,专门用来读取DWG文件内容。它的亮点在于不依赖AutoCAD环境,就能解析DWG里的图层、块、实体、标注等结构化数据。对于移动端来说,最常见的诉求是在Android设备上做一个DWG图纸浏览器,用户选一个文件,app解析出里面的图形对象,再通过OpenGL或自定义View渲染出来。
我这里的需求更聚焦:只是读取DWG文件里的图层列表、块定义和基础实体信息,然后展示给用户。DWG格式有多复杂?从AutoCAD R13到最新版本,格式一直在变,libredwg是目前开源社区里兼容性做得最好的解析库之一,它比libdxfrw这类库的格式覆盖更全。所以我从一开始就锁定了libredwg。
但是直接用现成的预编译库不可能,因为Android生态里根本没有“通用版”的libredwg预编译包。你必须自己动手,用Android NDK里的交叉编译器,在开发机上把libredwg编译成target为ARM架构的.so或.a文件。这个过程就叫交叉编译。
1.2 工具链方案:NDK内置工具链 vs 独立工具链
交叉编译的第一步是选工具链。Android官方推荐的方式就是直接用NDK包里的工具链。旧版本NDK(r17及以下)提供过一个make-standalone-toolchain脚本,用来生成独立工具链,很多老教程会让你这么干。但从NDK r18开始,官方彻底移除了gcc,全面转向clang,并且不推荐再手动生成独立工具链了。
我实测下来的结论是:直接用NDK自带的toolchain目录,配合环境变量,是最省事、最不容易出错的方案。不要自己折腾独立工具链,因为配置SYSROOT、CROSS_COMPILE这些变量很容易搞混,而且不同NDK版本之间的路径结构有差异。
那怎么调用NDK里的交叉编译器?核心是设置这么几个环境变量:
export NDK=/opt/android-ndk-r23c export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64 export TARGET=aarch64-linux-android export API=24 export AR=$TOOLCHAIN/bin/llvm-ar export CC=$TOOLCHAIN/bin/$TARGET$API-clang export AS=$TOOLCHAIN/bin/llvm-as export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++ export LD=$TOOLCHAIN/bin/ld export RANLIB=$TOOLCHAIN/bin/llvm-ranlib export STRIP=$TOOLCHAIN/bin/llvm-strip export SYSROOT=$TOOLCHAIN/sysroot注意这里的$TARGET$API-clang不是随便拼的,NDK在r23c里确实同时提供了aarch64-linux-android24-clang和aarch64-linux-android24-clang++这两个可执行文件。API=24对应Android 7.0,如果你的app最小支持版本更低,可以改成21,但要注意Android 5.0之前不支持64位arm64指令集,所以如果选择armeabi-v7a,得用armv7a-linux-androideabi$API-clang。
1.3 交叉编译的整体链路设计
交叉编译libredwg不是编译一个库就完事,它有一整条依赖链。libredwg有个可选的依赖项是libxml2,用来支持读取XML格式的DWG(DWG文件其实有个XML伴生格式)。如果你只是读取纯二进制DWG,理论上可以不编libxml2,但libredwg在configure时如果找到libxml2,会启用额外的XML读写功能;如果没有,也能编译,只是部分API不可用。
我建议还是把libxml2编上,因为DWG文件的组成比很多人想象的复杂——它里面除了图形数据,还有属性、扩展数据、代理实体等,这些信息用XML接口读取会更方便。而且libxml2本身也是c库,同样需要交叉编译。再加上libxml2又依赖zlib,所以完整的依赖链是:zlib → libxml2 → libredwg。
这个依赖链看起来长,但每层都是标准Autotools项目,套路一模一样:运行configure指定交叉编译参数,然后make,再make install到一个统一的前缀目录。把这个流程跑通一次,后面任何“C库交叉编译到Android”的需求都能照方抓药。
2. 环境准备:NDK版本选型、ABI确定与依赖库梳理
2.1 NDK版本怎么选:优先r23c,兼容性与稳定性之间取平衡
NDK版本的选择直接影响交叉编译的顺利程度。太老的版本(r17之前)用gcc,虽然有些人觉得gcc的生态更熟,但官方已经停止维护,编译新版libredwg时会出现各种“编译器不支持某特性”的报错。太新的版本(r25、r26)又可能对老项目不友好,因为新版NDK默认的minSdkVersion和libc++版本都有调整。
我自己用的是r23c,这个版本有几个优点:第一,它是最后一个同时保留“通用sysroot”结构的版本,配置起来不折腾;第二,clang版本是12.0.5,对Autotools项目的兼容性非常好;第三,网上遇到问题时,r23c对应的资料最多。如果你用r26、r27,会发现sysroot路径和链接器参数都变了,很多老教程的步骤直接失效。
安装NDK的方式也很简单,Android Studio自带的SDK Manager里就能装,也可以直接去官方页面下载压缩包。我建议直接下载压缩包解压到一个纯英文路径,路径里千万别有中文和空格,否则configure脚本极容易因为路径解析失败报错。项目里也别把NDK放在C:\Program Files这类有空格的位置,这是用血的教训换来的建议。
2.2 ABI选型:只编arm64-v8a还是三架构全上?
Android的ABI有armeabi-v7a、arm64-v8a、x86、x86_64四种,现代设备的实际情况是:arm64-v8a已经覆盖了99%的真机,x86/x86_64主要给模拟器用,armeabi-v7a是给老设备用的。
如果你只是做内部工具或者Demo,建议只编arm64-v8a。原因很简单:每多编一个ABI,编译时间、APK体积都会翻倍。如果以后要上Google Play,Play要求必须包含arm64-v8a,其他ABI看需求加。但要注意,只在APK里打arm64-v8a,会导致x86模拟器上装不了应用,调试时需要用ARM镜像或者真机。
交叉编译时对应的TARGET环境变量:
| ABI | TARGET值 | 说明 |
|---|---|---|
| arm64-v8a | aarch64-linux-android | 现代手机主流,API 21起支持 |
| armeabi-v7a | armv7a-linux-androideabi | 32位ARM老设备 |
| x86_64 | x86_64-linux-android | 64位模拟器 |
| x86 | i686-linux-android | 32位模拟器 |
我这次的实践只针对arm64-v8a,即TARGET=aarch64-linux-android,这也是最典型、最值得优先掌握的一种。
2.3 依赖库清单:libxml2和zlib为什么要先搞定
libredwg在configure阶段会通过pkg-config或者直接查找头文件的方式探测libxml2。如果探测不到,它不会报错,只是禁用XML相关功能。这会导致什么后果?DWG里的Extensible Data(XDATA)和某些字符串信息读不出来,对于多数场景来说可能无所谓,但如果你做的是图纸数据导出工具,这就有问题了。
所以我的建议是:既然目标是做一个能真正解析DWG的Android库,就老老实实把依赖编全。需要准备两份源码:
- zlib:太常见了,Android源码里有内置版本,但交叉编译时还是要自己编一份,因为libxml2链接时需要它的头文件和静态库。zlib版本用1.2.13以上即可。
- libxml2:版本用2.11.x系列较稳,新版2.12+对Autotools的configure参数有调整,容易踩坑。
这两份源码都要交叉编译成arm64版本的产物,安装到同一个前缀目录(比如/home/user/android-libs/),这样后面libredwg的configure就能顺着CPPFLAGS和LDFLAGS找到它们。
3. libredwg交叉编译完整实操记录
3.1 第一步:交叉编译zlib
zlib是三者中结构最简单的,编译起来也最快。它的configure不是Autotools的configure,而是zlib自定义的脚本,需要一点特殊处理。
tar -xzf zlib-1.2.13.tar.gz cd zlib-1.2.13 export CC=aarch64-linux-android24-clang export AR=llvm-ar export RANLIB=llvm-ranlib export CFLAGS="-O3 -fPIC -I$SYSROOT/usr/include" ./configure --prefix=/home/user/android-libs/zlib --static make -j8 make install注意几个点:CC一定要指到带API版本的clang,不要只写aarch64-linux-android-clang,否则会找不到默认sysroot;CFLAGS里必须加-fPIC,因为后面要编进.so,没有位置无关代码会链接失败;--static只生成静态库,避免生成.so时又附带交叉编译的动态链接麻烦。zlib编译通常不用改源码,一次就能过。
如果make时报错“cannot find -lc”,说明SYSROOT没有配置正确或者TARGET和API组合有问题。检查一下环境变量,确保c库路径能通过llvm的搜索机制找到,最简单的办法是打印编译命令行,看它执行的到底是什么。
3.2 第二步:交叉编译libxml2
libxml2比zlib复杂不少,因为它的configure会去检测很多系统特性,比如iconv库。Android的Bionic libc本身没有完整的iconv实现,libxml2又需要iconv来处理多字节编码,所以这里经常出问题。
我尝试过两种方案:第一种是给libxml2加--without-iconv,让它强制不启用iconv,但这样会导致解析UTF-8以外的编码时出错。第二种是单独交叉编译一个libiconv,然后让libxml2链接它。这两个方案我都试过,最终建议用第二种,因为DWG文件里的字符串往往是本地编码(比如国标码),没有iconv会读乱码。
# 先编译libiconv tar -xzf libiconv-1.17.tar.gz cd libiconv-1.17 ./configure --host=aarch64-linux-android --prefix=/home/user/android-libs/iconv --disable-rpath --enable-static make -j8 make install # 再编译libxml2 tar -xzf libxml2-2.11.5.tar.gz cd libxml2-2.11.5 export CC=aarch64-linux-android24-clang export CPPFLAGS="-I/home/user/android-libs/iconv/include -I/home/user/android-libs/zlib/include" export LDFLAGS="-L/home/user/android-libs/iconv/lib -L/home/user/android-libs/zlib/lib" ./configure --host=aarch64-linux-android --prefix=/home/user/android-libs/libxml2 \ --with-iconv=/home/user/android-libs/iconv \ --with-zlib=/home/user/android-libs/zlib \ --without-python --without-lzma --disable-shared --enable-static make -j8 make install--without-python和--without-lzma是必须的:Python交叉编译到Android纯粹是自找麻烦,lzma同样需要额外依赖。--disable-shared --enable-static告诉它只编静态库,这也是本次实践的关键决定——libredwg最终要编成一个.so,所有依赖链都用静态库,能极大简化JNI层的链接问题。动态库嵌套会疯狂拉长加载时间,而且搞不好就出现dlopen找不到符号的问题。
3.3 第三步:libredwg本体configure与make
依赖库就绪后,libredwg的交叉编译就顺畅多了。首先从GitHub拉源码:
git clone https://github.com/LibreDWG/libredwg.git cd libredwg sh autogen.shautogen.sh会生成configure脚本,这一步需要本机装了autoconf、automake、libtool。如果你本机版本太旧,可能生成失败,建议先把这些工具升级到最新版本。
重点看configure参数:
export CC=aarch64-linux-android24-clang export CPPFLAGS="-I/home/user/android-libs/libxml2/include -I/home/user/android-libs/iconv/include -I/home/user/android-libs/zlib/include" export LDFLAGS="-L/home/user/android-libs/libxml2/lib -L/home/user/android-libs/iconv/lib -L/home/user/android-libs/zlib/lib" export LIBS="-lxml2 -liconv -lz" ./configure --host=aarch64-linux-android \ --prefix=/home/user/android-libs/libredwg \ --enable-shared --disable-static \ --disable-bindings --disable-python \ --without-libmagic有几个参数专门解释一下:
- --host=aarch64-linux-android是交叉编译的核心标志,告诉configure“我编译出来的程序跑在Android上”。
- --enable-shared --disable-static:libredwg本体编成动态库,因为最终要打包成.so给JNI用。依赖库是静态库,libredwg是动态库,这样最终产物只有一个libredwg.so。
- --disable-bindings:禁用swig生成的各语言绑定,我们在Android上只需要C API。
- --without-libmagic:magic库用于文件类型识别,Android上没有,必须关掉。
- LIBS="-lxml2 -liconv -lz"是给最终链接用的,如果不在configure阶段指定,后面make的时候可能会因为链接顺序问题找不到符号。
configure成功后会生成Makefile,然后make编译。这里建议加上-j8参数加快速度:
make -j8 make install整个编译过程大概两三分钟,取决于机器性能。编译完成后检查产物:
file /home/user/android-libs/libredwg/lib/libredwg.so如果输出显示“ELF 64-bit LSB shared object, ARM aarch64”,说明交叉编译成功。如果显示x86-64,那一定是configure时--host没生效,回去检查环境变量。
3.4 产物检查:用readelf验证依赖关系和ABI
编译完成后,还需要验证几个关键点:依赖的动态库、ABI架构、以及是否有undefined symbol。
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readelf -d libredwg.so主要看NEEDED里是否有libxml2.so、libc.so之外的东西。因为libxml2、iconv、zlib都是静态链进libredwg.so的,所以NEEDED应该只包含libc.so、libm.so、libdl.so这类Android系统自带的库。如果出现libxml2.so,说明链接方式搞错了,将来在Android上会找不到这个库。
再检查UndeFined symbols:
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-nm -D libredwg.so | grep " U "正常情况下undefined符号只应该涉及标准C库函数,如果有xmlParseFile这样明显来自libxml2的符号,就说明静态链接没生效。这种问题通常是因为libxml2编译时没编成静态库,或者链接顺序不对——静态库必须放在依赖它的动态库之后,这个顺序在Makefile里是固定的,很难手动改,所以最好的解决办法就是从一开始就保证libxml2的库文件里.a存在且.so不存在,这样链接器只能选静态库。
4. Android Studio集成:CMake配置与JNI封装细节
4.1 新建项目与CMakeLists.txt编写
交叉编译出来的.so,最终要放进Android项目里。这里有一个关键问题:libredwg的C API非常复杂,暴露给Java层的接口必须自己封装一层JNI。直接在Android Studio里写JNI C代码,再用CMake打包成另一个.so,链接libredwg.so,这是最清晰的结构。
在Android Studio中新建一个Native C++项目,然后在app/src/main/cpp/目录下放两个文件:CMakeLists.txt和native-lib.cpp(名字可以自定义)。
CMakeLists.txt的关键内容:
cmake_minimum_required(VERSION 3.18.1) project("dwgdemo") set(LIBREDWG_DIR ${CMAKE_SOURCE_DIR}/libredwg) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") add_library(libredwg SHARED IMPORTED) set_target_properties(libredwg PROPERTIES IMPORTED_LOCATION ${LIBREDWG_DIR}/${ANDROID_ABI}/libredwg.so ) add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib libredwg) find_library(log-lib log) target_link_libraries(native-lib ${log-lib})注意这里的${ANDROID_ABI}是CMake自动注入的变量,在app/build.gradle里配置好ABI筛选:
android { defaultConfig { externalNativeBuild { cmake { cppFlags "-std=c++17" // 这里只需要arm64-v8a,因为交叉编译只编了这个架构 abiFilters "arm64-v8a" } } } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" } } }把编译好的libredwg.so放到app/src/main/cpp/libredwg/arm64-v8a/目录下,Android Studio构建时就会把.so文件打包进APK。
4.2 JNI接口设计:要暴露哪些功能给Java层
libredwg的C API头文件是dwg.h,它的核心数据结构是dwg_struct和dwg_data。用dwg_read_file读取一个DWG文件后,就能遍历图层、块、实体。
但C API不适合直接暴露给Java,因为指针管理和内存释放很容易出问题。我的设计思路是:把复杂的C数据对象“拍平”成基础类型,通过JNI返回给Java层。例如,只暴露三个核心方法:
extern "C" JNIEXPORT jstring JNICALL Java_com_example_dwgdemo_DwgParser_loadFile(JNIEnv *env, jobject, jstring path); extern "C" JNIEXPORT jstring JNICALL Java_com_example_dwgdemo_DwgParser_getLayerNames(JNIEnv *env, jobject, jstring jsonStr); extern "C" JNIEXPORT void JNICALL Java_com_example_dwgdemo_DwgParser_close(JNIEnv *env, jobject);loadFile负责打开DWG文件并解析,getLayerNames返回一个JSON字符串,JSON里是图层名数组。用JSON作为JNI层的数据交换格式,能避免在C++和Java之间传自定义对象时的大量代码,缺点是序列化反序列化有一定开销,但图层名这种小数据量完全无压力。
native-lib.cpp里的大致逻辑:
#include <jni.h> #include <string> #include "dwg.h" static Dwg_Data *g_dwg = nullptr; extern "C" JNIEXPORT jboolean JNICALL Java_com_example_dwgdemo_DwgParser_loadFile(JNIEnv *env, jobject, jstring path) { const char *pathStr = env->GetStringUTFChars(path, nullptr); g_dwg = (Dwg_Data *)malloc(sizeof(Dwg_Data)); int error = dwg_read_file(pathStr, g_dwg); env->ReleaseStringUTFChars(path, pathStr); return error == 0 ? JNI_TRUE : JNI_FALSE; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_dwgdemo_DwgParser_getLayerNames(JNIEnv *env, jobject) { if (!g_dwg) return env->NewStringUTF("[]"); std::string result = "["; // 遍历图层列表,dwg_data结构里通过object链表访问 // 具体遍历方式取决于libredwg版本的API,这里只写思路 result += "]"; return env->NewStringUTF(result.c_str()); }注意dwg_read_file的具体API在不同版本有差异,有的是dwg_read_file(path, dwg),有的是dwg_read_file(path, dwg, &opts)。我用的git master版本的API是dwg_read_file(const char *filename, Dwg_Data *dwg),返回int。在使用前先查一下头文件的函数声明,这个细节别搞错。
4.3 Java层调用与全局状态管理
Java层定义一个DwgParser类,用静态native方法包装:
public class DwgParser { static { System.loadLibrary("native-lib"); } public static native boolean loadFile(String path); public static native String getLayerNames(); public static native void close(); public static List<String> parseDwg(File file) { boolean ok = loadFile(file.getAbsolutePath()); if (!ok) return Collections.emptyList(); String json = getLayerNames(); close(); // 用JSONArray解析后返回 return new ArrayList<>(); } }这里有个全局状态管理的隐患:g_dwg是全局指针,如果同一个app里多个地方同时调用parseDwg,会数据竞争。实际项目中应该用synchronized或者做成单线程调用。DWG解析本来就不是高频操作,加个锁问题不大。
释放内存同样重要,dwg_free(g_dwg)必须在解析完后调用,否则一个大型DWG文件可能占几百MB内存,很容易把app搞崩。close方法里做:
if (g_dwg) { dwg_free(g_dwg); free(g_dwg); g_dwg = nullptr; }5. 常见问题与排查技巧实录
5.1 configure阶段报错“cannot run C compiled programs”
这是交叉编译最经典的问题,因为configure脚本默认会尝试运行刚编译出来的测试程序,但运行环境是x86架构,根本跑不了ARM程序。解决方法是给configure加--host参数,或者设置环境变量ac_cv_exeext=
export ac_cv_exeext=大多数情况下只要--host写对了,configure自动会跳过运行步骤。如果还报这个错,检查configure命令有没有加--host=aarch64-linux-android,我遇过很多次写错了TARGET值导致configure认为cross-compile模式没开启。
5.2 编译时找不到头文件或库文件
这种现象一般是CPPFLAGS和LDFLAGS没设对。一个非常稳的经验是:在configure之前,手动用交叉编译器编一个简单的C文件,验证环境是否正常:
echo 'int main(){return 0;}' > test.c $CC test.c -o test如果这一步就失败,说明工具链本身有问题,后面所有操作都会失败;如果成功,再试一次带-I和-L的编译,验证依赖库路径是否顺畅。所有配置工作都在configure之前完成,编环境的时间不要省。
5.3 运行时UnsatisfiedLinkError问题
编译配置一切都正常,但app一运行就报java.lang.UnsatisfiedLinkError: dlopen failed: library "libxml2.so" not found。这个问题的根源就是libredwg.so对libxml2做了动态链接,但我们没有把libxml2.so打进APK。
排查方法:
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readelf -d libredwg.so | grep NEEDED如果看到libxml2.so,就需要回头重新链接。一个快速的补救方案是:把libxml2.so也放到CMake的导入库列表里,一起打包进APK。但这种方式在多个.so互相依赖时容易出问题,不如重编干净。
最彻底的解决方式:确保依赖库都以静态库形式存在,configure时指定LIBS=-lxml2等,且对应lib目录下不要放.so文件。我当时就在libxml2的lib目录下同时有libxml2.a和libxml2.so,链接器默认选.so,后面清理掉.so只留.a,问题一次性解决。
5.4 编译速度太慢和内存溢出
libredwg的源码量不小,不加-j参数单线程编译,能在开发机上磨蹭十几分钟。但-j参数也别贪多,我试过-j16,结果内存不够直接OOM崩溃。风险控制建议是-j8,同时确保本机RAM不低于8G。如果想进一步减少编译压力,可以修改configure参数,只编译需要的模块:
--disable-bindings --disable-python --disable-examples这些参数确实能减少编译时间,后面的dwg2dxf、dwgread这些命令行工具也是编译目标,但Android上用不到,全关掉。
5.5 DWG文件解析失败但又不报错
这个现象比较隐蔽:loadFile返回true,但解析出的图层和数据是空的。后来排查发现,libredwg默认开启了STRICT模式,遇到格式不标准的DWG文件会拒绝解析。针对这种情况,可以在loadFile时设置更宽松的选项:
Dwg_Read_Opts opts = {0}; opts.dxf_version = INVALID; opts.test = false; int error = dwg_read_file_opts(pathStr, &opts, g_dwg);注意dwg_read_file_opts这个函数在新版本里才有,老版本只有dwg_read_file。这本质上是个API版本兼容问题,建议尽量用git master最新代码,然后锁定提交哈希。
6. 从交叉编译到集成:最后的经验沉淀
整个流程走下来,最大的体会是:交叉编译的难点其实不在编译本身,而在依赖关系和ABI意识。libredwg这种标准的Autotools项目,只要工具链环境配置对了,configure和make一条路走到底,反而是在Android Studio集成阶段,CMake配置和JNI封装的一些小细节让人反复折腾。
如果让我重新做一遍,我会提前想清楚三件事:第一,最终产物选择静态库还是动态库,这决定依赖库的编译方式;第二,ABI只选arm64-v8a还是全架构,这决定后续调试和发布的成本;第三,JNI层的数据交换格式用JSON还是用自定义结构体,这决定封装代码的复杂度。
这三件事想明白,交叉编译就是按部就班的操作。我个人的建议始终是:先做一个最小的demo,native层只调用一个函数,验证.so能加载、能跑通,再逐步增加功能。别一上来就把所有API都封装完,否则任何一个环节出问题,排查范围会大得让你崩溃。
最后再分享一个小技巧:把交叉编译的所有configure脚本和命令整理成一个shell脚本,下次在另一台机器上只需要修改路径就能直接跑。这段经历本身也通用——编译zlib、libxml2、libredwg本质上都是同一套动作,掌握一次,以后想往Android上移植OpenSSL、FFmpeg、libcurl这类C库,思路完全一致。
本文还有配套的精品资源,点击获取