Android 原生库依赖解析Gradle插件

Stella981
• 阅读 772

由于官方的Android Gradle插件无法解析在dependencies中声明的.so库依赖,所以编译时不会把.so文件自动拷贝到jniLibs目录下,这个插件主要就是为了解决这个问题的,并且提供so文件重命名和abi过滤的实用功能.
另外如果你是使用maven和android-maven-plugin 构建Android项目,并且项目里面有native依赖库的,如果现在想转移到Gradle构建系统上来,那么这个插件正好合适. 开源项目地址:https://github.com/linsea/native-dependencies-plugin

##使用说明 1. 引入插件

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.com.github.linsea:native-dependencies-plugin:0.2.1"
  }
}

//在*主模块*项目中(比如APP项目中)应用插件,
//否则可能遇到Android官方插件中的这个BUG:https://code.google.com/p/android/issues/detail?id=158630
apply plugin: "com.github.linsea.native-dependencies"

2. 声明Native依赖库
Native依赖库的声明还是像通常一样写在dependencies里面,但要加上classifier和ext,比如:

dependencies {
    compile "org.ffmpeg:algorithm:1.2.3:armeabi-v7a@so"
}

如果你之前使用maven,应该明白这对应maven里面如下的声明:

<dependency>
    <groupId>org.ffmpeg</groupId>
    <artifactId>algorithm</artifactId>
    <version>1.2.3</version>
    <classifier>armeabi-v7a</classifier>
    <type>so</type>
</dependency>

3. 配置so文件的过滤与重命名规则
3.1 soFilters
在主模块的build.gradle文件中添加一个nativeso块,里面必须有一个soFilters属性,表示项目支持的平台架构种类, 通过这个属性区分so文件的类别属于哪个平台架构,以便拷贝时把相应类别的so文件放到jniLibs下面的相应目录中. 被依赖的so文件的文件名中必须包含这个类别标识符,比如所有armeabi-v7a平台的相应so文件,在仓库中文件的命名 必须包含有armeabi-v7a这个串,否则无法区别这个so文件到底是属于哪个平台的,也就无法拷贝.
3.2 renaming
通常Linux平台的so库文件有一定的命名规则,比如有lib前缀,但是有些开发者提供的库并没有按这些规则来命名,如果 命名与加载时没有对应起来,Android是加载不到so库的.如果APP中引用多个库,而命名又五花八门,为了能使我们把so库 拷贝到jniLibs下后Android可以加载到库,有时我们需要重命名so文件.
**renaming**就是定义命名规则的,一条renaming规则可以对应一个或多个so文件的命名,regex是以正则表达式的 方式匹配so文件名,如果匹配到任意一次,则so文件名不会再继续匹配下一条renaming规则. 重命名时可以定义 replaceAll或者replaceWith的方式,两者同时定义时, 仅replaceAll生效而忽略replaceWith. 以replaceAll方式重命名时,实际上是在so filename上执行String.replaceAll(regex,replaceAll). 而以replaceWith方式重命名时,则支持正则表达式的占位符($+数字表示)替换,功能更加强大,几乎可以满足所有命名需求. 具体可以参考Gradle文档中Copy的重命名.比如:

regex = '(.*)_OEM_BLUE_(.*)'
replaceWith = '$1$2'

prefix 表示so文件重命名后加入的前缀,一般需要lib前缀时可以配置这个.

注意
文件的重命名时,从上到下依次匹配规则,如果有一个匹配到了,则下面的规则会忽略跳过,如果最终一个规则都没有匹配到,则文件不会重命名.

以下是一个nativeso的配置示例及说明:

nativeso {

//.so filename MUST contains 'armeabi-v7a' or 'x86' to identify abi types
   soFilters = ['armeabi-v7a']     //['armeabi-v7a','x86']

//rename .so file: textsearch-1.2.3-armeabi-v7a.so -> libtextsearch.so
   renaming {
        prefix = 'lib'
        regex = 'textsearch-1.2.3-armeabi-v7a.*'
        replaceAll = 'textsearch' //not include ext .so
    }

//rename .so file: libhello-1.4.10-armeabi-v7a.so -> libhello.so
   renaming {
        regex = 'libhello(.*)'
        replaceWith = 'libhello'
    }

//rename .so file: libmysdk2-v7.0-armeabi-v7a.so -> libmysdk2.so
    renaming {
        regex = 'libmysdk(\\d+)-v(.*)'
        replaceWith = 'liblocSDK$1'
    }

//默认命名规则: name-version-armeabi-v7a.so -> libname.so
    renaming {
        prefix = 'lib'
        regex = '(.*)-([\\d\\.]+)-(.*)'
        replaceWith = '$1'
    }
}

##注意事项 插件会把so文件拷贝到jniLibs下对应的目录下,比如jniLibs/armeabi-v7a,而且会利用Gradle提供的缓存机制,提高编译速度,但是当依赖更新而过期时,需要重新下载并拷贝,在拷贝之前,插件会清空jniLibs目录.因此如果你的主模块项目已经包含了一些native依赖,并且.so文件已经放到jniLibs下的对应目录下,那么插件会同时把这些so文件也删除.为了避免这种情况发生,需要增加一个单独 的jniLibs目录给插件使用,比如:

android {
    sourceSets {
        main {
            //...
            jniLibs.srcDirs += 'src/main/nativeso' //一定要加在数组的最后
        }
    }

##使用技巧 1.插件增加了一个名为collectso的Task,并且利用了gradle的增量编译机制,如果文件是新的,Task不会重复执行.如果重命名时需要调试脚本,可以运行以下命令:

./gradlew -q --rerun-tasks collectso --info

查看插件的输出日志和重命名后的文件名,以便帮助定义重命名规则.

2.通常,如果你的APP仅支持有限的几种ABI,则其他所有不支持的ABI的so文件应该在APK打包中排除掉,否则在某些机型中无法加载 对应ABI上缺失的so,在android plugin 2.2及以上版本可以通过如下配置达到效果:

android {
    packagingOptions { //假设你的APP仅支持armeabi-v7a架构,则其他的所有架构的so需要排除之
        exclude '/lib/armeabi/**'
        exclude '/lib/arm64-v8a/**'
        exclude '/lib/x86/**'
        exclude '/lib/x86_64/**'
        exclude '/lib/mips/**'
        exclude '/lib/mips64/**'
    }
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这