Gradle实战-14-多渠道打包

多渠道

defaultConfig {
    ....
    // All flavors must now belong to a named flavor dimension.
    //flavorDimensions 是不能少的,如果有flavor 使用的情况下 否则会出现 kotlin not figured
    flavorDimensions "env"
    // dex突破65535的限制
    multiDexEnabled true
    // 默认是umeng的渠道
    manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
    ...
}
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="${APP_NAME}"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${UMENG_CHANNEL_VALUE}" />
       ...
</application>

manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"] 可以设置 meta-data 中的value 数据,这个数据一般是用在渠道分发使用。

sourceSets {
        main {
            //分模块存放资源
            res.srcDirs = ['src/main/res',
                           'src/main/res-ad',
                           'src/main/res-player'
            ]
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            resources.srcDirs = ['src/main/resources']
            aidl.srcDirs = ['src/main/aidl']
            renderscript.srcDirs = ['src/maom']
            //res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
            jniLibs.srcDir 'src/main/jniLibs'
            //对默认的so存放地址改成 app/libs目录
            //jniLibs.srcDirs =['libs']
        }
        //不同flavor的res目录    
        xiaomi.res.srcDirs = ['src/main/res-xiaomi']
        huawei.res.srcDirs = ['src/main/res-huawei']
        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('test')
        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.

        //debug.setRoot('build-types/debug')
        //release.setRoot('build-types/release')
}
//根据不同的Flavor和不同的module对App的资源进行替换和逻辑变换
//渠道Flavors,配置不同风格的app
productFlavors {
    xiaomi {

    }
    huawei{ //右键->Mark Directory as -> Resources Root

    }
}

//根据Flavor设置在清单文件中进行设置
productFlavors.all {
    flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name, APP_NAME: name]
}

如果我们想在build的时候,就按照某种 flavor 进行打包,那么就可以在 Build Variants 中选择对应的 flavor 版本,然后正常编译即可编译出对应的apk。

按照Flavor打包成不同的apk 

def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
    ...
    android.applicationVariants.all { variant ->
      variant.outputs.all {
          //这里的逻辑是在配置阶段结束之后进行的
          outputFileName = "luckyboy-${variant.buildType.name}-${releaseTime()}-${defaultConfig.versionName}-${variant.productFlavors[0].name}.apk"
      }
  }
  ...
}

执行不同的命令得到不同的apk

./gradlew assemble

./gradlew assembleDebug

./gradlew assembleRelease

./gradlew assembleXiaomi

./gradlew assembleXiaomiRelease

将打包好的release包改名并拷贝到指定的地方,如下是将app/build/outputs目录下的编译好的apk文件拷贝到用户的桌面的release文件目录下,且按照 variant 归类。

android {

   ...
    afterEvaluate {
        applicationVariants.all { variant ->
                def name = variant.name // xiaomiRelease
                def baseName = variant.baseName //xiaomi-release
                println "name : $name" //
                println "baseName: $baseName"
                Map<String, String> map = System.getenv()
                String userName = map.get("USER")
                def des = '/Users/'+userName + '/Desktop/release'
                //根据apk类型打包(release 和 debug 两个版本,一般供单个应用打包使用)
                if (variant.buildType.name == 'release') {
                    println "release打包类型"
                    def output = variant.outputs.first()
                    def apkName = "app-${variant.baseName}"+"-${variant.versionName}.apk"
                    //修改打包的apk文件名称
                    output.outputFileName = apkName
                    println "outputFileName "+output.outputFile.name
                    println "outputFile----->"+output.outputFile.path
                    def destFilePath = des+"/"+"${variant.baseName}"
                    println "destFile----"+destFilePath
                    //拷贝apk 文件到指定的目录
                    copy {
                        from output.outputFile.path
                        into destFilePath
                    }
                }
                if (variant.buildType.name == 'debug'){ //Debug打包类型 构建的是debug包
                    println "debug打包类型"
                }
        }
    }
...

}

签名,在项目的app目录下创建keystore目录,然后 build->Generated Signed  Bundle/Apk,然后按照提示创建进签名文件,并进行相应配置,注意在 Create New的时候填写的是 keystore 的全路径【目录地址是keystore路径,包含文件的名称和后缀名,后缀名可以是jks】。

android {

  ...
  
 //signingConfigs的前后顺序不能乱
  signingConfigs {
        release {
            storeFile file("keystore/release.jks")
            storePassword "admin123"
            keyAlias "luckyjoy"
            keyPassword "admin123"
        }
        debug {
            storeFile file("keystore/debug.jks")
            storePassword "admin123"
            keyAlias "luckyjoy"
            keyPassword "admin123"
        }
    }  

  buildTypes {
        release {
            zipAlignEnabled true
            // 移除无用的resource文件 如果要使用shrinkResources 必须使minifyEnabled为true
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            buildConfigField "boolean", "DEBUG_ENABLE", 'false'
            buildConfigField("String", "BASE_URL", '"http://one.luckyjoy.com"')
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            signingConfig signingConfigs.debug
            buildConfigField "boolean", "DEBUG_ENABLE", 'true'
            buildConfigField("String", "BASE_URL", '"http://one.luckyjoy.com"')
        }
    }


}

其他

android {
  ...
  lintOptions {
        所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
        //checkReleaseBuilds false
        //错误发生后停止gradle构建
        abortOnError false
  }

  dexOptions {
        //最大堆内存
        javaMaxHeapSize '2048M'
        //预编译
        preDexLibraries = true
        //线程数
        threadCount = 16
        dexInProcess = true
  }
 ...
}
//以下的两个def方法可以放在总工程路径下的build.gradle里使用ext置成全局多个
//Module调用(通过rootProject.ext.xxx调用获取桌面上的keystore路径)
def getKeyStorePath(){
    Map<String, String> map = System.getenv()
    String userName = map.get("USER")
    def des = '/Users/'+userName+'/'+'keystore.jks'
    return des
}

//获取AndroidManifest中的versionName
def getVersionName(Project project){
    def xmlFile = project.file("src/main/AndroidManifest.xml")
    def rootManifest = new XmlSlurper().parse(xmlFile)
    def versionName = rootManifest['@android:versionName']
    return versionName
}

通过代码获取设置的渠道名称

val channel:String?
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
channel = appInfo.metaData.getString("UMENG_CHANNEL")
print("channel $channel")
tv_brand.text = channel

遇到的问题:

在项目执行 ./gradlew assemble 的时候,控制台报错 编译器 (1.8.0_144) 中出现异常错误。在查看其他的项目并进行对比之后,发现修改 compileSdkVersion 和 buildToolsVersion 以及 targetSdkVersion 之后【都修改成29,对应的buildToolsVersion 改成 29.0.2】,竟然神奇的好了,不清楚原因是什么,可能构建工具需要和对应的JDK版本要相匹配,否则也会出现问题。

以下是build文件的全部内容

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.luckyboy.productflavor"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        // All flavors must now belong to a named flavor dimension.
        //flavorDimensions 是不能少的,如果有flavor 使用的情况下 否则会出现 kotlin not figured
        //flavorDimensions "env", "kind"
        flavorDimensions "env"
        // dex突破65535的限制
        multiDexEnabled true
        // 默认是umeng的渠道
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    sourceSets {
        main {
            //分模块存放资源
            res.srcDirs = ['src/main/res',
                           'src/main/res-ad',
                           'src/main/res-player'
            ]
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            resources.srcDirs = ['src/main/resources']
            aidl.srcDirs = ['src/main/aidl']
            renderscript.srcDirs = ['src/maom']
            //res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
            jniLibs.srcDir 'src/main/jniLibs'
            //对默认的so存放地址改成 app/libs目录
            //jniLibs.srcDirs =['libs']
        }
        xiaomi.res.srcDirs = ['src/main/res-xiaomi']
        huawei.res.srcDirs = ['src/main/res-huawei']
        // Move the tests to tests/java, tests/res, etc...
        //instrumentTest.setRoot('test')
        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.

        //debug.setRoot('build-types/debug')
        //release.setRoot('build-types/release')
    }

    //signingConfigs的前后顺序不能乱
    signingConfigs {
        release {
            storeFile file("keystore/release.jks")
            storePassword "admin123"
            keyAlias "luckyjoy"
            keyPassword "admin123"
        }
        debug {
            storeFile file("keystore/debug.jks")
            storePassword "admin123"
            keyAlias "luckyjoy"
            keyPassword "admin123"
        }
    }

    buildTypes {
        release {
            zipAlignEnabled true
            // 移除无用的resource文件 如果要使用shrinkResources 必须使minifyEnabled为true
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            buildConfigField "boolean", "DEBUG_ENABLE", 'false'
            buildConfigField("String", "BASE_URL", '"http://one.luckyjoy.com"')
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            signingConfig signingConfigs.debug
            buildConfigField "boolean", "DEBUG_ENABLE", 'true'
            buildConfigField("String", "BASE_URL", '"http://one.luckyjoy.com"')
        }
    }

    //根据不同的Flavor和不同的module对App的资源进行替换和逻辑变换
    //渠道Flavors,配置不同风格的app
    productFlavors {
        xiaomi {
           dimension 'env'
        }
        huawei{ //右键->Mark Directory as -> Resources Root
           dimension 'env'
        }
//        charge{
//           dimension 'kind'
//        }
//        free {
//           dimension 'kind'
//        }
    }

    //根据Flavor设置在清单文件中进行设置
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name, APP_NAME: name]
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
//    kotlinOptions {
//        jvmTarget = '1.8'
//    }

//  applicationVariants.all { variant ->
//      variant.outputs.all {
//          //这里的逻辑是在配置阶段结束之后进行的
//          outputFileName = "luckyboy-${variant.buildType.name}-${releaseTime()}-${defaultConfig.versionName}-${variant.productFlavors[0].name}.apk"
//      }
//  }

    afterEvaluate {
        applicationVariants.all { variant ->
                def name = variant.name // xiaomiRelease
                def baseName = variant.baseName //xiaomi-release
                println "name : $name" //
                println "baseName: $baseName"
                Map<String, String> map = System.getenv()
                String userName = map.get("USER")
                def des = '/Users/'+userName + '/Desktop/release'
                //根据apk类型打包(release 和 debug 两个版本,一般供单个应用打包使用)
                if (variant.buildType.name == 'release') {
                    println "release打包类型"
                    def output = variant.outputs.first()
                    def apkName = "app-${variant.baseName}"+"-${variant.versionName}.apk"
                    //修改打包的apk文件名称
                    output.outputFileName = apkName
                    println "outputFileName "+output.outputFile.name
                    println "outputFile----->"+output.outputFile.path
                    def destFilePath = des+"/"+"${variant.baseName}"
                    println "destFile----"+destFilePath
                    //拷贝apk 文件到指定的目录
                    copy {
                        from output.outputFile.path
                        into destFilePath
                    }
                }
                if (variant.buildType.name == 'debug'){ //Debug打包类型 构建的是debug包
                    println "debug打包类型"
                }
        }
    }

    lintOptions {
        所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
        //checkReleaseBuilds false
        //错误发生后停止gradle构建
        abortOnError false
    }

    dexOptions {
        //最大堆内存
        javaMaxHeapSize '2048M'
        //预编译
        preDexLibraries = true
        //线程数
        threadCount = 16
        dexInProcess = true
    }
}

//以下的两个def方法可以放在总工程路径下的build.gradle里使用ext置成全局多个
//Module调用(通过rootProject.ext.xxx调用获取桌面上的keystore路径)
def getKeyStorePath(){
    Map<String, String> map = System.getenv()
    String userName = map.get("USER")
    def des = '/Users/'+userName+'/'+'keystore.jks'
    return des
}

//获取AndroidManifest中的versionName
def getVersionName(Project project){
    def xmlFile = project.file("src/main/AndroidManifest.xml")
    def rootManifest = new XmlSlurper().parse(xmlFile)
    def versionName = rootManifest['@android:versionName']
    return versionName
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
}

参考:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值