Android Studio Library 模块中 Native 代码进行 debug 的一些坑

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

前言

如果项目中存在多个module,那么在application模块中依赖library模块,并且library模块中有native代码的时候,假设你需要debug library模块中的这些native代码,正常情况下,这部分native代码是不能直接被debug的。导致这个问题的根本原因是因为即使在运行application模块的debug构建时,其依赖的library模块并不是以debug构建,而是以release构建。

项目结构例子如下图所示

project.png

解决不能debug的方式有两种。

1、不进行StripSymbolDebug

Gralde中有一个Task叫transformNativeLibsWithStripDebugSymbolFor${BuildType},其对应的代码在com.android.build.gradle.internal.transforms.StripDebugSymbolTransform中,翻看代码可以发现如下代码


     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

     
if (excludeMatchers .stream() .anyMatch(m -> m .matches(Paths .get(path)))) {
FileUtils .mkdirs(strippedLib .getParentFile());
FileUtils .copyFile( input, strippedLib);
} else {
stripFile( input, strippedLib, abi);
}
private void stripFile(@NonNull File input, @NonNull File output, @Nullable Abi abi)
throws IOException {
FileUtils .mkdirs( output .getParentFile());
if (abi == null) {
FileUtils .copyFile( input, output);
return;
}
ProcessInfoBuilder builder = new ProcessInfoBuilder();
builder .setExecutable(stripExecutables .get(abi));
builder .addArgs( "--strip-unneeded");
builder .addArgs( "-o");
builder .addArgs( output .toString());
builder .addArgs( input .toString());
ILogger logger = new LoggerWrapper(project .getLogger());
ProcessResult result = new GradleProcessExecutor(project) .execute(
builder .createProcess(),
new LoggedProcessOutputHandler(logger));
if (result .getExitValue() != 0) {
logger .warning( "Unable to strip library '%s', packaging it as is.",
input .getAbsolutePath());
FileUtils .copyFile( input, output);
}
}

当满足excludeMatchers中的正则匹配时,该Task执行的是直接拷贝so文件,而不满足时,则执行的是strip操作。

而excludeMatchers是在构造函数中被赋值的


     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

     
@NonNull
private final Set<PathMatcher> excludeMatchers;
public StripDebugSymbolTransform(
@NonNull Project project,
@NonNull NdkHandler ndkHandler,
@NonNull Set<String> excludePattern) {
this.excludeMatchers = excludePattern.stream()
.map( StripDebugSymbolTransform::compileGlob)
.collect(ImmutableCollectors.toImmutableSet());
checkArgument(ndkHandler.isConfigured());
for(Abi abi : ndkHandler.getSupportedAbis()) {
stripExecutables.put(abi, ndkHandler.getStripExecutable(abi));
}
this.project = project;
}

查看其构造函数被调用的地方


     
1
2
3
4
5
6
7
8
9

     
TransformManager transformManager = scope.getTransformManager();
GlobalScope globalScope = scope.getGlobalScope();
transformManager.addTransform(
tasks,
scope,
new StripDebugSymbolTransform(
globalScope.getProject(),
globalScope.getNdkHandler(),
globalScope.getExtension().getPackagingOptions().getDoNotStrip()));

从上代码可以看到忽略列表是从PackagingOptions中的DoNotStrip传入。

那么问题就好办了,我们只需要在library模块和application模块中加入忽略strip的正则匹配即可,如下


     
1
2
3
4
5
6
7
8
9
10
11
12
13

     
android {
//...
packagingOptions {
doNotStrip "*/armeabi/*.so"
doNotStrip "*/armeabi-v7a/*.so"
doNotStrip "*/arm64-v8a/*.so"
doNotStrip "*/x86/*.so"
doNotStrip "*/x86_64/*.so"
doNotStrip "*/mips/*.so"
doNotStrip "*/mips64/*.so"
//...
}
}

值得注意的是,library模块和application模块中的gradle都需要加入。

但是问题又来了,我们发布到maven的时候,是不需要执行这个的,因此,最好配置一个开关,且这个开关不会被提交到git中去,因此local.properties是最合适的


     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

     
boolean isDebug() {
boolean ret = false
try {
Properties properties = new Properties()
File file = project.rootProject. file( 'local.properties')
if (! file.exists()) {
return false
}
properties.load( file.newDataInputStream())
String debugStr = properties.getProperty( "debug")
if (debugStr != null && debugStr.length() > 0) {
ret = debugStr.toBoolean()
}
} catch (Throwable throwable) {
throwable.printStackTrace()
ret = false
}
project.logger.error( "[${project.name}]Debug:${ret}")
return ret
}

然后在local.properties中加入debug=true,修改packagingOptions配置为


     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

     
android {
//...
if (isDebug()) {
packagingOptions {
doNotStrip "*/armeabi /*.so"
doNotStrip "*/armeabi-v7a /*.so"
doNotStrip "*/arm64-v8a /*.so"
doNotStrip "*/x86 /*.so"
doNotStrip "*/x86_64 /*.so"
doNotStrip "*/mips /*.so"
doNotStrip "*/mips64 /*.so"
//...
}
}
}

2、让Library模块的BuildType随Application模块的BulidType而构建

除了以上的方式,其实还有一种方式,那就是让Library不进行默认的release构建,而是随Application的BuildType而改变,当Application的BuildType为debug时,Library也进行debug构建,当Application的BuildType为release时,Library则进行release构建,要做到这样,需要显示声明一下compile的configuration。

查看Google的相关文档可以发现怎么做:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

application模块中的依赖原来是这样的


     
1

     
compile project( ':library')

将其修改为


     
1
2

     
releaseCompile project( path: ':library', configuration: 'release')
debugCompile project( path: ':library', configuration: 'debug')

然后在library模块中的gradle中加入一行配置,表示不使用默认的


     
1
2
3
4
5
6
7

     
android {
//...
defaultConfig {
publishNonDefault true
//...
}
}

配置完成之后,就可以进行愉快的debug了,但是还没完,对的,需要判断是否是debug,那么以上配置就变成了这样


     
1
2
3
4
5
6

     
if (isDebug()) {
releaseCompile project( path: ':library', configuration: 'release')
debugCompile project( path: ':library', configuration: 'debug')
} else {
compile project( ':library')
}

     
1
2
3
4
5
6
7
8
9

     
android {
//...
defaultConfig {
if (isDebug()) {
publishNonDefault true
}
//...
}
}

这里之所以加入debug判断,是因为发maven的时候,如果存在publishNonDefault=true,Maven发布插件将把这些额外的variant作为额外的包发布。这意味着它发布到一个maven仓库并不是真正的兼容。我们应该只向一个仓库发布一个单一的 variant。因此发布maven的时候,我们需要关闭这个配置项。

但是为了避免他人不知道这个情况,因此我们最好主动做一次检测,在项目根目录下的build.gradle加入检测代码


     
1
2
3
4
5
6
7
8
9
10
11
12

     
allprojects. each { project ->
project.afterEvaluate {
def uploadArchivesTask = project.tasks.findByName( "uploadArchives")
if (uploadArchivesTask) {
uploadArchivesTask. doFirst {
if (isDebug()) {
throw new RuntimeException( "uploadArchives must disable debug options in local.properties first!")
}
}
}
}
}

一旦运行uploadArchives这个Task的时候,如果是debug,则直接扔异常,不让其发布。

http://fucknmb.com/2017/05/11/Android-Studio-Library%E6%A8%A1%E5%9D%97%E4%B8%ADNative%E4%BB%A3%E7%A0%81%E8%BF%9B%E8%A1%8Cdebug%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91/
Android library native 代码不能调试解决方法汇总 android native开发会碰到native代码无法调试问题,而app主工程中的native代码是可以调试的。如果项目中存在多个module,那么在application模块中依赖library模块,并且library模块中有native代码的时候,当debug library模块中的这些native代码时可能会发现断点打不进去。导致这个问题的根本原因是因为即使在运行application模块debug构建时,其依赖的library模块并不是以debug构建,而是以release构建。 阅读详情

相关推荐

Android系统架构之Native Libraries

Android系统中,Native Libraries(本地库)提供了一系列用C和C++编写的核心库,它们为Android应用程序提供了底层的支持。通过JNI,开发者可以利用C/C++的高性能特性,实现对底层资源的直接访问,提升应用的性能。:使用Android NDK(Native Development Kit)编译C/C++代码为库文件(.so文件)。:在Java代码中,使用JNI(Java Native Interface)加载本地库,并调用其中的函数。

bai915290475的博客 1143

使用命令编译Gradle Daemon

使用Android Studio编译报如下异常 IOException: https://dl.google.com/android/repository/addons_list-3.xml java.net.ConnectException: Connection refused: connect IOException: https://dl.google.com/android/repository/addons_list-2.xml java.net.ConnectException: Conn

figosoar的专栏 4099

Android】Unable to strip the following libraries, packaging them as they are:

Unable to strip the following libraries

TheDaybreak的专栏 3341

Android Gradle中packagingOptions选项的使用

packagingOptions常见的设置项有exclude、pickFirst、doNotStrip、merge。 1.exclude,过滤掉某些文件或者目录不添加到APK中,作用于APK,不能过滤aar和jar中的内容。 比如: 2.pickFirst,匹配到多个相同文件,只提取第一个。只作用于APK,不能过滤aar和jar中的文件。 比如: 3.doNotStrip,可以设置某些动态库不被优化压缩。(有对应库的符号表) 比如: 4.merge,将...

程序猿老樊的博客 3860

AS Library 使用NDK 的一些 Unable to strip library (暨部分深入了解gradle的机制)

1、这文章写得很好,读完后可以更了解gradle的机制。 与下面文章一起阅读,可以更好理解,其中有一个句: Android Library按照编译方式可分为“release”、“debug”版本;按照不同的产品可分为“demo”、“full”版本,例如有demoDebug和fullRelease等版本 https://blog.csdn.net/tantion/article/detai...

T的专栏 1万+

Android.bp 语法,设置-g, debug,android.mk 编译 debug 版本

同时,Android.mk可以引用Android.bp中的模块,反之Android.bp不能引用Android.mk中的模块。name :编译出的模块的名称,类似于Android.mk中的LOCAL_MODULE。cflags:编译flag,类似于Android.mk中的LOCAL_CFLAGS。srcs:源文件,类似于Android.mk中的LOCAL_SRC_FILES。Android.bp使用单行注释//和多行注释/* */两种方式。

sun007700的专栏 1695

Android Studio编译C++代码——Release与Debug版本的记录

问题 部分C++代码库,Release版本与Debug版本速度差异非常大,拿之前的Dlib的人脸检测来说,Debug版本在手机上跑速度基本上是15秒1帧,而Release版本差不多是1秒2帧,这个速度差异非常的大。 AS上始终编译不出Release版本的库文件,又不会在Linux下去编译SO文件(技术不行,,,加上Android运行环境和Linux也有一点区别) 经过多天的摸索,大概有了思路

shen的博客 1万+

使用Android Studio与Eclipse开发NDK的对比

最近开发的一个项目中用到了JNI,所以针对Android的两大IDE开发NDK做了一下对比。Android Studio开发NDK的配置我使用的是稳定版的2.0的插件,默认情况下它是不支持NDK的,即如果你在build.gradle中配置了ndk属性是无法别识别的。这个时候我们只需要在gradle.properties中配置以下属性即可android.useDeprecatedNdk=true当我第

pcsxk的专栏 2648

安卓NDK开发实战:从JNI原理到性能优化与避指南

在移动开发领域,性能优化与底层硬件交互是高级开发的核心诉求。通过理解JNI(Java Native Interface)这一跨语言通信的桥梁,开发者能够突破Java虚拟机的限制,直接调用C/C++代码,实现性能敏感型计算和复用庞大的成熟生态库。其技术价值在于为应用提供了与系统底层、硬件及现有C/C++生态对话的能力,从而在图像处理、音视频编解码等场景中发挥关键作用。例如,在`ndk编译opencv3`这类任务中,正是通过NDK将计算机视觉库集成到安卓应用,解决了密集计算和算法复用的需求。同时,在`安卓缓存r

weixin_34296641的博客 365

Android Studio 使用指南:从安装配置到常见问题解决

3、配置工程,最好使用studio自带的clone,暂时遇到未解问题,“使用本地git下载工程,再用studio打开,会出一些难解决的问题,暂时未知”链接:https://developer.android.com/studio?Ubuntu和win建议下载zip包绿色。1、解压后windowns执行。

m0_56306892的博客 432

Unity项目导出Android Studio全流程:JNI交互、Gradle配置与原生开发实战

在移动应用与游戏开发中,跨平台引擎与原生平台的深度集成是提升应用能力的关键技术路径。其核心原理在于通过引擎提供的桥接接口(如JNI)实现脚本语言与原生代码的互操作,从而突破引擎本身的功能限制。这一技术的核心价值在于能够灵活调用平台专属API、集成第三方原生SDK,并实现高性能模块的定制开发。典型的应用场景包括接入特定支付服务、集成定制音视频处理库、实现复杂设备交互或与现有原生应用模块融合。本文聚焦于Unity引擎与Android原生开发的无缝衔接,详细解析了从项目导出、Gradle工程配置到JNI双向通信的

weixin_30565327的博客 528

解决调试无法进入Native代码

解决调试无法进入Native代码

cooler20p的博客 678

Android Studio Jni开发(三)Native方法动态注册

今天来介绍下动态注册

supernovaExp的博客 834

React Native跨平台开发:环境搭建与实战指南

跨平台开发框架通过共享代码库显著提升移动应用开发效率,其中React Native凭借其独特的架构设计脱颖而出。该框架利用JavaScriptCore引擎实现与原生组件的直接通信,既保留了接近原生的性能表现,又支持热更新等关键特性。在工程实践中,React Native特别适合中低复杂度的消费类APP开发,如电商、社交等场景,同时为已有React技术栈的团队提供平滑过渡方案。本文将详细解析从Node.js环境配置到Android/iOS开发环境搭建的全流程,涵盖常见问题解决方案与生产力工具推荐,帮助开发者快

weixin_34384681的博客 491

Android Studio之让人懵逼的错误:Failed to load native library 'native-platform.dll' for Windows 10 x86.

在使用Android Studio的时候,经常会遇到各种莫名其妙的错误,让人一脸懵逼,无从下手,有时候整一天都弄不明白,既浪费了时间又让人身心俱疲。下面我把我这次遇到的奇葩问题和解决办法贴到这里,希望能够帮到大家,让大家1分钟就爬成功!(心疼自己个儿)1.错误描述: Error:Unable to start the daemon process. This problem might be

k_bb_666的博客 5070

Android Studio 报错 executing external native build for cmake xxx CMakeLists.txt

今天带着沉重的心情记录一下这两天的郁闷,因为就在昨天,我把android studio 搞坏了 昨天还是可用的,就因为我升级了什么东西……… 然后就一直报错 A problem occurred configuring project ‘:app’ executing external native build for cmake xxx CMakeLists.tx...

风云的的博客 5万+

clion编译android native项目代码

android studio创建一个native c++临时项目,然后使用其生成的ninja编译参数到clion中进行编译步骤: 1.编译参数生成自android studio编译生成文件.cxx/Debug/…//metadata_generation_command.txt 2.提示clang++: error: unsupported option ‘-fPIE’ for target 'x86_64-w64-windows-gnu’时,可添加下面flags消除错误提醒 -D...

qq_15050521的博客 1649

android ndk library,Android NDK - Library not found

I work with Android Studio and Android NDK.My folder "src/main/jni" contains the following files:Android.mkApplication.mkHelloWorld.cppApplication.mkAPP_ABI := allAndroid.mkLOCAL_PATH := $(call my-dir...

weixin_32160507的博客 421

如何调试Android Native Framework

http://www.open-open.com/lib/view/open1484468370862.html 半年前写了一篇文章,介绍 如何调试Android Framework ,但是只提到了Framework中Java代码的调试办法,但实际上有很多代码都是用C++实现的;无奈当时并并没有趁手的native调试工具,无法做到像Java调试那样简单直观(gdb+eclipse/ida

Linux Android Web 850
上一篇: aapt2 资源 compile 过程
下一篇: libnghttp2 NDK 交叉编译
Omni-Space
博客等级 码龄11年 2157粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值