Android依赖导入全攻略

在我们开发安卓项目的时候,不会所有的功能都自己去造轮子,经常要使用到各种的其他包,其中有谷歌给我们提供的各种support包,也有各种第三方的功能库,有时候我们自己也会将一些功能封装成包。这些包存在和导入的形式也多种多样,有远程仓库的,有直接拷贝到本地的,jar包、aar包、so包等。所幸我们都可以在主工程和各个Module的build.gradle里进行统一管理。本文将在Android Studio3.0环境下来汇总下这些用法。

预备知识

先来看下Android Gradle plugin 3.0几个引入依赖的方法:

Implementation

对于使用了该命令编译的依赖,对该项目有依赖的项目将无法访问到使用该命令编译的依赖中的任何程序,也就是将该依赖隐藏在内部,而不对外部公开。

使用implementation会使编译速度有所增快:比如我在一个library中使用implementation依赖了gson库,然后我的主项目依赖了library,那么,我的主项目就无法访问gson库中的方法。这样的好处是编译速度会加快,我换了一个版本的Gson库,但只要library的代码不改动,就不会重新编译主项目的代码。

api

等同于compile指令

compileOnly

等同于provided,只在编译时有效,不会参与打包,不会包含到apk文件中。可以用来解决重复导入库的冲突(下文会提到)。

更多细节内容可以看下这篇文章

远程仓库依赖

我们先来看下主工程下的build.gradle文件

buildscript {
    ext.kotlin_version = '1.1.51'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

引入远程仓库依赖是很方便的,但在之前我们需要声明远程仓库的地址。上面有两个仓库地址的声明,一个在buildscript {},另一个在repositories {}。看代码中系统给我们的注释就知道:前者是gradle脚本自身执行所需依赖(Gradle插件),后者是项目本身需要的依赖(普通代码库)。所以如果你没有引入远程的Gradle插件,那么就不用在buildscript {}下的dependencies下添加依赖。

关于Gradle插件的开发可以看下这篇文章:Gradle自定义插件

再来看下几种远程依赖的添加方式:

 implementation 'commons-lang:commons-lang:2.6'
 
 implementation group: 'com.google.code.guice', name: 'guice', version: '1.0'
 
 implementation('org.hibernate:hibernate:3.1') {
        //不同版本同时被依赖时,那么强制依赖这个版本的,默认false
        force = true
        //exclude可以设置不编译指定的模块,有三种写法:
        exclude module: 'cglib' 
        exclude group: 'org.jmock' 
        exclude group: 'org.unwanted', module: 'iAmBuggy' 
        //禁止依赖的传递,gradle自动添加子依赖项(依赖包所需的依赖),设置为false,则需要手动添加每个子依赖项,默认为true。
        transitive = false
    }

同样的配置下的版本冲突,会自动使用最新版;而不同配置下的版本冲突,gradle同步时会直接报错。可使用exclude、force解决冲突。
比如你同时依赖了两个版本的v7包:

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:appcompat-v7:23.1.1'

最终只会使用26.1.0版本。但是如implementation 'com.android.support:appcompat-v7:23.1.1',和androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.1',所依赖的com.android.support:support-annotations版本不同,就会导致冲突。除了可以用exclude、force解决外,也可以自己统一为所有依赖指定support包的版本,不需要为每个依赖单独排除了:

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support') {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion '26.1.0'
            }
        }
    }
}

编译期注解的依赖--annotationProcessor

用过butterknife或者Dagger的同学可能对这种annotationProcessor引入方式有所印象,这种方式是只在编译的时候执行依赖的库,但是库最终不打包到apk中。结合编译期注解的作用,他是用来生成代码的,本身在运行时是不需要的。

本地依赖

jar包

jar包依赖的导入还是比较简单的:

  • implementation files('hibernate.jar', 'libs/spring.jar')//列出每个jar包的相对路径
  • implementation fileTree(dir: 'libs', include: ['*.jar'])//列出包含jar包的文件夹路径

但和远程仓库依赖引入方式不同,如果本地同时存在两个不同的jar包,或者本地已有jar包,再去远程依赖不同版本的jar包,就会报错。

解决方式:将其中的一个采用compileOnly替换implementation。顾名思义,compileOnly只在编译时起作用,不会包含到APK里面,在运行时也就避免找到重复的类了。

aar包

和jar包不同,aar包存放的路径声明和依赖引入是分开的:

repositories {
    flatDir {
        dir "../${project.name}/libs"
    }
}
dependencies {  
    implementation(name: 'aar名字', ext: 'aar')  
} 

如果aar包有很多,也可以一样象jar包统一添加一个文件夹下的所有包:

    def dir = new File('app/libs')
    dir.traverse(
            nameFilter: ~/.*\.aar/
    ) { file ->
        def name = file.getName().replace('.aar', '')
        implementation(name: name, ext: 'aar')
    }

当一个library类型的module需要引用aar文件时,也要在所在模块的build.gradle文件中加入上面的话,但是当其他 Module引用此library的module时,也需要在他的build.gradle中加入如下配置,否则会提示找不到文件:

repositories {  
    flatDir {  
        dirs 'libs', '../包含aar包的模块名/libs'  
    }  
}  

即如果当前Module需要一个aar包内容,不论aar包是不是在当前Module中,都需要在build.gradle中声明它所在的路径。如果项目中这样的Module比较多,每个都需要声明路径,不便于管理的话,推荐在项目的根build.gradle中统一添加,将所有包含aar包的模块名列出,这样不论是本Module或其他Module都不需要单独配置路径了:

allprojects {
    repositories {
        jcenter()
        google()
        flatDir {
             dirs "../moudle-A/libs,../moudle-B/libs,../moudle-C/libs".split(",")
        }
    }
}

so文件

这个和jar包差不多,声明下so文件的存放路径就行了:

   sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

或者直接在main目录下新建jniLibs目录,这是so文件默认的放置目录,不过不常用。值得一提的是aar包里面也可以包含so文件,但依赖这种包含so文件的aar包时不需要做特定的配置,编译时so文件会自动包含到引用AAR压缩包的APK中。

但比较特殊的一点是,so文件需要放到具体的ABI目录下,不能直接放libs目录下所以你见到的结果可能是这样的:

QQ20180414-233737@2x.png

所有的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的so文件。所以为了减小包体积,为了减小 apk 体积,可以只保留 armeabi 一个文件夹。但如果你想引入多个平台的,那么需要保持 so 文件的数量一致,就是说 armeabi 文件下的每个so文件都要在armeabi-v7a下找到对应的so文件,但这样apk包的体积就会增大。

还有一种做法是生成指定ABI版本的APK,然后按需上传到应用商店,让用户自己选择下载适合自己手机的版本,这个可能更多的用在安卓游戏APP上,build.gradle配置如下:

android {
    ... 
    splits {
        abi {
            enable true  //启用ABI拆分机制
            reset()  //重置ABI列表为只包含一个空字符串
            include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //与include一起使用来可以表示要使用哪一个ABI
             universalApk
             true//是否打包一个通用版本(包含所有的ABI)。默认值为 false。
        }
    }
 
    // ABI的code码
    project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
 
    android.applicationVariants.all { variant ->
        // 最终标记
        variant.outputs.each { output ->
            output.versionCodeOverride =
                    project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
        }
    }
 }

问题和小结

1.aar包中的资源文件重复了

资源文件重复了,主工程的资源文件会直接覆盖aar包中的文件,并且不会有任何报错或者提示,最终aar包中也会直接用主工程的资源文件,所以需要注意命名方式。暂时没有更好的解决方法。

2.AndroidManifest合并错误

同样也是发生在aar包上, Android Studio 项目每个module中都可以有一个AndroidManifest.xml文件,但最终的APK 文件只能包含一个 AndroidManifest.xml 文件。在构建应用时,Gradle 构建会将所有清单文件合并到一个封装到 APK 的清单文件中。aar包的清单文件和我们的app清单文件属性冲突时:用tools:replace="属性名"解决。

3.annotationProcessor与compileOnly的区别

上文说了annotationProcessor与compileOnly都是只编译并不打入apk中,他俩到底有什么区别呢?扮演的角色不一样,annotationProcessor作用是编译时生成代码,编译完真的就不需要了,compileOnly是有重复的库,为的是剃除只保留一个库,最终还是需要的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 说明 本文主要介绍和Gradle关系密切、相对不容易理解的配置,偏重概念介绍。部分内容是Android特有的(例如...
    jzj1993阅读 15,352评论 1 62
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,293评论 6 344
  • 前言 为什么需要学Gradle? Gradle 是 Android 现在主流的编译工具,虽然在Gradle 出现之...
    真笨笨鱼阅读 1,459评论 0 0
  • 每一个人都想融入到别人的话语里 也许是为了寻找一份不宁静 每一个人都会感觉自己是孤独的 想在别人的生活里开心而去遗...
    田萍阅读 166评论 1 2