AGP,Android Gradle Plugin,安卓Gradle插件,在根build.gradle中引入的classpath就是Android的gradle插件
classpath "com.android.tools.build:gradle:7.0.3"
android {
compileSdk 31
defaultConfig {
applicationId "com.study.sensortrackapp"
applicationIdSuffix "apk"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
manifestPlaceholders = [APP_NAME:"我的APP"]
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
1 defaultConfig
先从android这个节点开始,这些节点都是从com.android.application这个插件中提供的;defaultConfig是默认配置,这些信息是保存在BuildConfig这个类中
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.study.sensortrackapp";
public static final String BUILD_TYPE = "debug";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}
applicationId:这个应用的唯一表示,默认就是当前app的包名,但不是类的包名
applicationIdSuffix:给applicationId添加一个后缀
minSdk:当前应用的最小版本,不能安装在低于minSdk版本的手机上
targetSdk:目标版本,其实是为了做兼容使用;设置了目标版本31,意味着可以使用31版本的新特性,但是同样也可以兼顾 [ minSdk,targetSdk) 的老用户,所以在一些代码逻辑中会出现,如果超过某个版本,就会使用哪个API
versionCode:版本号,版本升级的时候,会用来判断是否是新版本
versionName:版本名称
manifestPlaceholders:清单文件占位符,map类型的数据结构
//Manifest文件中使用
android:label="${APP_NAME}"
//Merged Manifest中查看
android:label = 我的APP
2 productFlavors
直译是产品风味,其实就是项目构建的变种,例如在多渠道打包的时候,需要打华为应用市场,或者小米应用市场的渠道包,就需要在productFlavors中做相对应的配置
flavorDimensions "channel"
flavorDimensions就是对变种的描述,也可以看做是变种的维度,defaultConfig可以看做是默认的产品风味,channel就是从渠道维度的变种,而且声明了维度就必须要使用,否则会报错
productFlavors{
huawei{
dimension "channel"
manifestPlaceholders = [CHANNEL_NAME: "华为渠道"]
}
xiaomi{
dimension "channel"
manifestPlaceholders = [CHANNEL_NAME: "小米渠道"]
}
}
在productFlavors中,就创建变种的类型,像华为渠道、小米渠道分类,dimension就是维度的类型,这里是channel
这样在构建的变种中,就有4类产品,在切换构建类型的时候,Manifest清单文件也在跟着变化
applicationVariants.all{ variant->
variant.outputs.all{ output->
println "$output"
}
}
通过applicationVariants可以遍历获取这4个变种,获取每个变种的输出文件,就是之前讲到过的packageDebug任务
如果要对每个渠道打包,自定义输出的apk类型,就需要在输出之前 就把apk名称定义好
applicationVariants.all{ variant->
variant.outputs.all{ output->
def outputFile = output.outputFile
if(outputFile != null && outputFile.name.endsWith(".apk")){
//遍历所有的产品风味
productFlavors.each{ flavor->
outputFileName = "v${defaultConfig.versionName}_${flavor.manifestPlaceholders.CHANNEL_NAME}.apk"
}
}
}
}
在打包签名之后,对应的会生成对应的huawei文件夹,其中就包含了我们自定义的apk名称
在productFlavors中定义的属性,像dimension、manifestPlaceholders,可以直接拿到,如果想要更多的值,都可以放在manifestPlaceholders数组中
productFlavors.dimension
productFlavors.manifestPlaceholders.CHANNEL_NAME
对应的在huwei/BuildConfig中就多了一个属性FLAVOR
public static final String FLAVOR = "huawei";
3 buildTypes
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildTypes可以看做是构建模式,主要就是分为debug模式和release模式,如果想要自定义属性,可以使用buildConfigField,在BuildConfig中添加属性
buildConfigField("String","type","\"3\"")
4 sourceSets
sourceSets用来指定,某个文件夹所在的路径;例如我们在libs文件夹下添加了so文件,或者在jniLibs下添加了so文件,都需要执行这个路径
sourceSets {
main{
jniLibs.srcDirs = ['jniLibs']
java.srcDirs = ['src']
exclude 'TestActivity'
}
}
这是在src/main文件下的路径,当然也可以设置清单文件的路径;如果在打包的时候,不想某些文件打进包,可以使用exclude排除
5 Dex分包
当我们的项目在打包apk的时候,如果方法数超过65535就会导致打包apk失败(一个dex方法数 > 65535),这种情况当然也有概率出现,当我们的项目依赖很多三方库或者组件化开发,方法数很有可能超过65535
其中的原因就是,dexOpt(apk检索工具)会把每一个类的方法id检索起来,保存在一个链表中,然后这个链表的长度使用short类型来保存的,这就使得方法数id不能超过65535
在Android 5.0之前(minSdk 21),使用Dalvik虚拟机将应用限制为每个 APK 只能使用一个 classes.dex 字节码文件,这样就非常容易超限,所以为了绕开这个限制,需要给Dex分包,下面就是Dex分包的步骤
步骤1:multiDexEnabled true
defaultConfig {
......
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
步骤2:添加MultiDex依赖
implementation 'androidx.multidex:multidex:2.0.1'
步骤3:MultiDexApplication创建
class MyApplication : MultiDexApplication(){
}
在清单文件中声明这个application之后,就绕开了这个63335(64K)限制
在Android 5.0之后,就已经默认开启了分包,不再需要做这些设置
我们看到这里是分了3个包,classes.dex、classes2.dex、classes3.dex,其中classes.dex中主要是放置了一些依赖项的源码
classes2.dex主要是存放了一些R文件
classes3.dex包中就是我们自己写的代码
6 aar打包 + 上传maven
先创建一个module,比如我最近要埋点了,我自己创建一个module,我要打包一个aar让大家用
module创建完成之后,通过task/assemble打包成一个aar,添加到app项目的libs目录下
implementation files('libs/SensorHelper-release.aar')
使用这样的方式依赖aar会有问题,需要声明一下路径
Caused by: org.gradle.internal.typeconversion.UnsupportedNotationException: Cannot convert the provided notation to a File or URI: {name=SensorHelper-release.aar, ext=aar}.
The following types/formats are supported:
- A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
- A String or CharSequence URI, for example 'file:/usr/include'.
- A File instance.
- A Path instance.
- A Directory instance.
- A RegularFile instance.
- A URI or URL instance.
声明路径需要在setting.gradle中的Repository中声明,这样使用aar就是直接从lib仓库里找aar
flatDir {
dirs 'libs'
}
一般情况下,lib都是存在自己公司的maven仓库里,需要的时候,从maven仓库里拉下来,都是aar依赖,这里需要依赖maven插件
如果想要上传maven仓库,需要使用maven的插件,在gradle 7.0以上的版本需要使用”maven-publish“这个插件,7.0以下的版本使用maven插件
id 'maven-publish'
在gradle7.0以上,使用了publishing来发布maven私服,7.0以下使用的是uploadArchives,那么这里就着重介绍publishing发布。
publishing{
repositories {
maven {
allowInsecureProtocol true //非https仓库
url MAVEN_URL
credentials {
username USERNAME
password PASSWORD
}
}
}
publications{
Publish(MavenPublication){
groupId = "com.github.sensor"
def projectName = rootProject.getName()
artifactId = projectName//该aar包的名称
version = "1.0.0"//
}
}
}
repositories是maven私服的配置,url是maven仓库的地址,用户名和密码主要是用来拉取代码时的鉴权;
publications主要就是用来发布aar,这里感觉讲的比较粗,我需要再细化一点儿再发布出来,这个技术其实也是很重要的,我先要搭建一个自己的maven私服