Gradle实战:发布aar包到maven仓库
查看原文:http://blog.csdn.net/u010818425/article/details/52441711
Gradle实战系列文章:
《Gradle基本知识点与常用配置》
《Gradle实战:Android多渠道打包方案汇总》
《Gradle实战:不同编译类型的包同设备共存》
《Gradle实战:执行sql操作hive数据库》
aar简介
aar文件是Google为Android开发所设计的一种library格式,全名为Android Archive Library,与Java Jar Library不同的是,aar除了java code之外还包含资源文件,即xml文件、图片、文字等。
本文着重介绍发布过程和遇到的一些坑及其解决方案,文中的maven仓库是指公司搭建的maven仓库,如果要发布到jCenter或maven central,可以参考文章最后的“深入学习“。
1. 准备工作
- 开发工具:Android Studio;
- 复习《Gradle基本知识点与常用配置》,本文会用到gradle中全局属性设置、文件读取、shell指令执行等相关知识点;
工程必须是lib工程,即该工程对应的build.gradle文件中要引用:
apply plugin: 'com.android.library'
在根目录的build.gradle文件中添加
allprojects {
apply plugin: 'idea'
apply plugin: 'maven' configurations {
deployerJars
}
} configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'//不使用缓存,使用仓库中最新的包
} subprojects { //表示除主工程外所有子模块
dependencies {
deployerJars "org.apache.maven.wagon:wagon-http:2.2"
}
} ext { //仓库选择标记
repoType = "remote" //发布到远程仓库(下文中会用到)
// repoType = "local" //发布到本地仓库,方便调试,避免调试期间频繁上传到maven仓库(下文中会用到)
}
在gradle.properties文件中添加:
releaseRepositoryUrl=xxx //正式包仓库地址(下文中会用到)
snapshotRepositoryUrl=xxx //测试包仓库地址(下文中会用到)
repositoryGroup=com.company.appname // 定义要上传的aar所在仓库的Group,可自定义,但后续引用处要与此一致
在工程根目录下新建一个名为“mavenAccount.properties”文件,并将该文件加入到ignore 中,该文件用于存放访问maven仓库的账户和密码以及本地仓库地址,只有该模块的开发者才有权发布该aar包。
repositoryUserName=xxx
repositoryPassword=xxx
localRepositoryUrl=file:///Users/admin/Documents/Android/repo/
2. 编写上传脚本
生成aar包
在工程根目录下新建一个名为“release-as-aar.gradle”的文件,其中脚本如下:
uploadArchives() {
repositories {
mavenDeployer { configuration = configurations.deployerJars println 'repoType : ' + rootProject.ext.repoType if ((rootProject.ext.repoType).equals("remote")) { //发布到远程仓库
snapshotRepository(url: snapshotRepositoryUrl) { // 测试包 //从本地文件读取仓库账号和密码
def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('repositoryUserName') && props.containsKey('repositoryPassword')) {
def repositoryUserName = props['repositoryUserName']
def repositoryPassword = props['repositoryPassword']
authentication(userName: repositoryUserName, password: repositoryPassword) println '上传到远程仓库'
} else {
println '没有发布权限'
}
} else {
println '没有发布权限'
}
} repository(url: releaseRepositoryUrl) { // 正式包
def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('repositoryUserName') && props.containsKey('repositoryPassword')) {
def repositoryUserName = props['repositoryUserName']
def repositoryPassword = props['repositoryPassword']
authentication(userName: repositoryUserName, password: repositoryPassword) println '上传到远程仓库'
} else {
println '没有发布权限'
}
} else {
println '没有发布权限'
}
}
} else { // 发布到本地仓库
def localRepositoryUrl
def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('localRepositoryUrl')) {
localRepositoryUrl = props['localRepositoryUrl']
snapshotRepository(url: localRepositoryUrl)
repository(url: localRepositoryUrl) println '上传到本地仓库'
} else {
println '没有发布权限'
}
} else {
println '没有发布权限'
}
}
}
}
}
生成jar包
在工程根目录下新建一个名为“release-as-jar.gradle”的文件,其中脚本如下:
task androidJavadocs(type: Javadoc) {
failOnError = false
source = android.sourceSets.main.java.srcDirs
ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath += files(ext.androidJar)
} task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
} task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
} uploadArchives {
repositories {
mavenDeployer { configuration = configurations.deployerJars println 'repoType : ' + rootProject.ext.repoType if ((rootProject.ext.repoType).equals("remote")) { //发布到远程仓库
snapshotRepository(url: snapshotRepositoryUrl) { def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('repositoryUserName') && props.containsKey('repositoryPassword')) {
def repositoryUserName = props['repositoryUserName']
def repositoryPassword = props['repositoryPassword']
authentication(userName: repositoryUserName, password: repositoryPassword) println '上传到远程仓库'
} else {
println 'sorry,你没有上传aar包的权限'
}
} else {
println 'sorry,你没有上传aar包的权限'
}
} repository(url: releaseRepositoryUrl) {
def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('repositoryUserName') && props.containsKey('repositoryPassword')) {
def repositoryUserName = props['repositoryUserName']
def repositoryPassword = props['repositoryPassword']
authentication(userName: repositoryUserName, password: repositoryPassword) println '上传到远程仓库'
} else {
println 'sorry,你没有上传aar包的权限'
}
} else {
println 'sorry,你没有上传aar包的权限'
}
}
} else {//发布到本地仓库
def localRepositoryUrl
def File propFile = new File('../mavenAccount.properties')
if (propFile.canRead()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('localRepositoryUrl')) {
localRepositoryUrl = props['localRepositoryUrl']
snapshotRepository(url: localRepositoryUrl)
repository(url: localRepositoryUrl) println '上传到本地仓库'
} else {
println 'sorry,本地仓库路径不存在'
}
} else {
println 'sorry,本地仓库路径不存在'
}
}
}
}
} artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
3. 子模块中相关配置
在子模块的build.gradle文件中添加:
group repositoryGroup
//version '0.0.1'
version '0.0.1-SNAPSHOT' //表示测试版,正式发版时去掉“-SNAPSHOT” //打成aar格式
apply from: '../release-as-aar.gradle' //引用上传插件 //打成jar格式
//apply from: '../release-as-jar.gradle'
4. 打包上传
- 编译通过后,打开android studio自带的终端,进入相应的module目录下,输入:
gradle uploadArchives
5. 使用aar
在需要引用aar包的工程中,根目录的build.gradle文件中进行如下配置:
allprojects {
repositories {
// jcenter(); //注释jcenter,表示不直接从jcenter仓库获取,而是通过公司私服仓库去获取
maven {
name 'xxx' //key与value之间有空格
url 'xxx' //key与value之间有空格
}
mavenLocal();
}
}
在子模块的build.gradle文件中进行如下引用:
dependencies {
compile group: repositoryGroup, name: 'xxx', version: '0.0.1', ext: 'aar', changing: true
}
6. 踩到的坑
问题一:上传时找不到服务器
上传时需关闭android studio的翻墙代理设置,且注释settings.gradle中自动生成的代理服务器相关配置,否则上传时会报找不到仓库服务器的错误。
问题二:aar包无法更新
有时上传了最新的snapshot包,引用的地方也sync、clean了,但引用的还是旧的包,此时需要删除“~/.gradle”中的相关记录。为方便执行,我们可以在应用工程根目录的build.gradle文件中,采用shell命令删除,该命令会在你执行clean操作时先执行:
task deleteDescriptors(type: Exec) { //执行shell命令
executable "sh"
args "-c", "rm -rf ~/.gradle/caches/modules-2/metadata-2.16/descriptors/com.company.appname"
//此处的“com.company.appname“就是之前定义的“repositoryGroup“。
} task clean(type: Delete, dependsOn: deleteDescriptors) { //clean工程时顺带执行上述任务
delete rootProject.buildDir
}
此时,再clean一下,引用的就是最新的aar包了。
问题三:无法设置debug编译类型
在lib工程中无论怎么设置编译类型,最后生成的aar包中始终都是release版本,该问题见google反馈。既然不可设置编译类型,我们可以在aar包代码中通过反射来获取应用的编译类型:
private Object getBuildConfigValue(Context context, String fieldName) {
try {
Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
} //使用
String buildType = getBuildConfigValue(ctx,"BUILD_TYPE").toString();
if (!TextUtils.isEmpty(buildType) && buildType.equals("debug")) { // debug
...
} else { // release
...
}
但是,这里面还有一个坑,系统版本在4.4以下的设备中,该方法无法获得包名,会抛空指针错误。以下我们给出完整的解决方案:
public class BuildConfigProvider { private static Context sContext; private static String packageName; public static String getBuildType() {
String buildType = (String) getBuildConfigValue("BUILD_TYPE");
if ("debug".equals(buildType)) {
buildType = "debug";
}
if ("release".equals(buildType)) {
buildType = "release";
}
return buildType;
} public static final boolean isDebug() {
return BuildConfig.DEBUG;
} /**
* 通过反射获取ApplicationContext
*
* @return
*/
private static Context getContext() {
if (sContext == null) {
try {
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
final Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread");
final Object activityThread = currentActivityThread.invoke(null);
final Method getApplication = activityThreadClass.getDeclaredMethod("getApplication");
final Application application = (Application) getApplication.invoke(activityThread);
sContext = application.getApplicationContext();
} catch (Exception e) {
e.printStackTrace();
}
} return sContext;
} /**
* 通过反射获取包名
*
* @return
*/
private static String getPackageName() {
if (packageName == null) {
try {
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
final Method currentPackageName = activityThreadClass.getDeclaredMethod("currentPackageName");
packageName = (String) currentPackageName.invoke(null);
} catch (Exception e) {
packageName = getContext().getPackageName();
}
} return packageName;
} public static Object getBuildConfigValue(String fieldName) {
try {
Class<?> clazz = Class.forName(packageName + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
return "";
}
}
当然,有人可能会说,既然可以通过反射得到
ApplicationContext,就没必要再去反射获得包名了,这里只是提供不同的解决方案以作参考。问题四:多包共存模式下获得编译类型为空
在上一篇博客《 Gradle实际应用(二):同名包共存》中,我们可以在一个设备中安装同一个应用不同编译类型的包。但是,非release包中我们获得的包名是带有编译类型后缀的(如“com.company.appname.debug“),而编译类型我们是通过反射获取,“BuildConfig“所在的包名还是原始的、不加后缀的包名(如“com.company.appname“),此时我们拿到的编译类型为空,那么我们可以在获取包名后做一个检查:
private static String checkPackageName(String packageName) {
String[] temp = packageName.split("\\.");
String sub = temp[temp.length - 1];
//如果多包共存模式,剔除包名中的后缀
if (sub.equals("debug")) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < temp.length - 1; i++) {
sb.append(temp[i]);
if (i != temp.length - 2) {
sb.append(".");
}
}
packageName = sb.toString();
}
return packageName;
}
Gradle实战:发布aar包到maven仓库的更多相关文章
- 如何发布jar包到maven中央仓库
自使用maven以来,没少使用maven中央仓库中的各种jar包,方便有效,但是咱们也不能总是只取不予,也应该懂得奉献,当你写好了一个十分好用的jar包,想贡献出去给大家使用的时候,应该怎么做呢?当然 ...
- Android业务组件化之Gradle和Sonatype Nexus搭建私有maven仓库
前言: 公司的业务组件化推进的已经差不多三四个月的时间了,各个业务组件之间的解耦工作已经基本完成,各个业务组件以module的形式存在项目中,然后项目依赖本地的module,多少有点不太利于项目的并行 ...
- 手动添加jar包到maven仓库
引言: 虽然配置了maven以后可以通过索引的方式自动下载jar包到本地maven仓库,从而使项目中直接使用本地仓库里面的架包, 但是这一招并不是每一次都灵应,也有遇到了失败的时候,当遇到失败的时候, ...
- 手动安装jar包到maven仓库
1.手动安装jar包到maven仓库 本地没有下载安装maven,但是eclipse已经集成的maven. 选中任何一个maven项目,右键/Run as/maven build... 在Goals输 ...
- 发布Jar包到maven中央仓库
什么是maven中央仓库 maven是java世界最流行的构建工具,构建内容囊括了一个java项目的整个生命周期.其中最重要的功能就是依赖管理,maven通过一个类似云的ftp站点统一管理所有java ...
- maven 发布jar包到远程仓库
有的时候我们需要发布一些自己写的相关jar包到maven私服,供项目组使用. 首先在setting.xml文件添加,这里 注意 要保证该账户有发布的权限 <servers> <ser ...
- 将Gradle项目发布到Jcenter和Maven Central
Jcenter和Maven Central 为了方便我们理解Android studio是如何帮助我们获取开源库的,我们需要理清几个概念.Apache Maven是Apache开发的一个工具,提供了用 ...
- gradle上传jar包到maven公共仓库
首先这里说的中央仓库 是指的 https://issues.sonatype.org/ 而不是maven私服. 其次是使用gradle上传jar包,maven上传,网上有很多教程,这里不做赘述. 首选 ...
- 发布Jar包到中央仓库
参考流程 https://blog.csdn.net/qq_36838191/article/details/81027586 备份还原 gpg-keys https://blog.rathena.c ...
随机推荐
- 行为识别(action recognition)相关资料
转自:http://blog.csdn.net/kezunhai/article/details/50176209 ================华丽分割线=================这部分来 ...
- https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
一:什么是https SSL(Security Socket Layer)全称是加密套接字协议层,它位于HTTP协议层和TCP协议层之间,用于建立用户与服务器之间的加密通信,确保所传递信息的安 ...
- 清除SQL Management Studio记住的用户名和密码
SQL Server Management Studio 2008 delete the file C:\Users\%username%\AppData\Roaming\Microsoft\Micr ...
- XSLT模糊查询函数contains不区分大小写,for-each排序
代码如下: <xsl:for-each select="//NewDataSet/map/area[contains(translate(@alt, 'ABCDEFGHIJKLMNOP ...
- kindeditor 上传图片 显示绝对 路径
在前台页面上 效果图 另外附上 urlType 属性的 参数说明: 改变站内本地URL,可设置空.relative.absolute.domain. 空为不修改URL,relative为相对路径,ab ...
- 无责任Windows Azure SDK .NET开发入门篇三[使用Azure AD 管理用户信息]
三.使用Azure AD管理用户信息 在上一章我们采用OpenID的方案和Azure AD交互进行身份验证,本章节我们继续了解如何在Azure AD中创建用户,列出用户信息,修改用户信息和删除用户信息 ...
- js页面文字选中后分享到新浪微博实现
demo您可以狠狠地点击这里:js文字选中分享到新浪微博demo 方法与代码 选中即分享的功能看上去比较高级,其实实现是相当简单的.其中的会让人头大,一般人也不感兴趣的原理这里就直接跳过.这个js文字 ...
- JSON初探
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意 ...
- Drupal 7.31 SQL注入漏洞利用具体解释及EXP
有意迟几天放出来这篇文章以及程序,只是看样子Drupal的这个洞没有引起多少重视,所以我也没有必要按着不发了,只是说实话这个洞威力挺大的.当然.这也是Drupal本身没有意料到的. 0x00 首 ...
- Codeforces Round #288 (Div. 2)D. Tanya and Password 欧拉通路
D. Tanya and Password Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/508 ...