美文网首页ITBOXandroid studioAndroid开发实战总结
Gradle多渠道打包(动态设定App名称,应用图标,替换常量,

Gradle多渠道打包(动态设定App名称,应用图标,替换常量,

作者: Wing_Li | 来源:发表于2016-08-16 10:51 被阅读17211次

如果本文帮助到你,本人不胜荣幸,如果浪费了你的时间,本人深感抱歉。
希望用最简单的大白话来帮助那些像我一样的人。如果有什么错误,请一定指出,以免误导大家、也误导我。
本文来自:http://www.jianshu.com/users/320f9e8f7fc9/latest_articles
感谢您的关注。

最近有个需求一次要打包9个类型的App,而且常量和String.xml都有变量。虽然之前也是一直存在变量,但是每次也仅仅只打包一个。这让我每次改变量,打包9个。要是以后每次都打包9次,我得疯了。
根据之前的了解,gradle 应该是可以解决这个问题的。所以就仔细研究了一番。


先放一个完整的 多渠道/多环境 打包的配置,然后再来讲解。

实现了:

  1. 不同环境,不同包名;
  2. 不同环境,修改不同的 string.xml 资源文件;
  3. 不同环境,修改指定的常量;
  4. 不同环境,修改 AndroidManifest.xml 里渠道变量;
  5. 不同环境,引用不同的 module。

先放一个完整的配置,可以参考:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion '22.0.1'

    // 签名文件
    signingConfigs {
        config {
            keyAlias 'lyl'
            keyPassword '123456'
            storeFile file('../lyl.jks')
            storePassword '123456'
        }
    }

    // 默认配置
    defaultConfig {
        //applicationId "com.lyl.app"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0.0"

        signingConfig signingConfigs.config
        multiDexEnabled true
        
        // gradle 3.0.0 以上需要有这个
        // flavorDimensions "app"
    }

    // 多渠道/多环境 的不同配置
    productFlavors {
        dev {
            // gradle 3.0.0 以上需要有这个
            // dimension "app"
            
            // 每个环境包名不同
            applicationId "com.lyl.dev"
            // 动态添加 string.xml 字段;
            // 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!
            resValue "string", "app_name", "dev_myapp"
            resValue "bool", "isrRank", 'false'
            // 动态修改 常量 字段
            buildConfigField "String", "ENVIRONMENT", '"dev"'
            // 修改 AndroidManifest.xml 里渠道变量
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "dev"]
        }
        stage {
            // gradle 3.0.0 以上需要有这个
            // dimension "app"
        
            applicationId "com.lyl.stage"

            resValue "string", "app_name", "stage_myapp"
            resValue "bool", "isrRank", 'true'

            buildConfigField "String", "ENVIRONMENT", '"stage"'

            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "stage"]
        }
        prod {
            // gradle 3.0.0 以上需要有这个
            // dimension "app"
        
            applicationId "com.lyl.prod"

            resValue "string", "app_name", "myapp"
            resValue "bool", "isrRank", 'true'

            buildConfigField "String", "ENVIRONMENT", '"prod"'

            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "prod"]
        }
    }

    dexOptions {
        incremental true
        // javaMaxHeapSize "4g"
    }

    //移除lint检测的error
    lintOptions {
        abortOnError false
    }

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

    buildTypes {
        debug {
            signingConfig signingConfigs.config
        }

        release {
            buildConfigField("boolean", "LOG_DEBUG", "false")
            minifyEnabled false
            zipAlignEnabled true
            //移除无用的resource文件
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config

            // 批量打包(gradle 3.0.0 以下)
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        //输出apk名称为:渠道名_版本名_时间.apk
                        def fileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
            
            // 批量打包(gradle 3.0.0 以上)
            // android.applicationVariants.all { variant ->
            // variant.outputs.all {
            //     outputFileName = // "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"
            // }
        }
        }
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.facebook.android:facebook-android-sdk:4.0.0'
    compile project(':qrscan')
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.google.code.gson:gson:2.3'
    compile files('libs/android-async-http-1.4.6.jar')
    compile 'com.google.android.gms:play-services:7.5.0'
    compile 'com.android.support:support-annotations:22.1.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'de.hdodenhof:circleimageview:2.1.0'
}

接下来我们来详细看看修改特定的字段。

不同环境的设置基本都是在 productFlavors 里设置的,
而且在里面你想添加多少个环境都可以。

1. 不同环境,不同包名;

productFlavors {
    dev {
        applicationId "com.lyl.dev"
    }

    stage {
        applicationId "com.lyl.stage"
    }

    prod {
        applicationId "com.lyl.prod"
    }
}

这里注意,在 defaultConfig 中,大家应该都是写了个默认的 applicationId 的。
经测试,productFlavors 设置的不同环境包名会覆盖 defaultConfig 里面的设置,
所以我们可以推测,它执行的顺序应该是先执行默认的,然后在执行分渠道的,如果冲突,会覆盖处理,这也很符合逻辑。

<br />

2. 不同环境,添加 string.xml 资源文件;

利用 resValue 来定义资源的值,顾名思义 res 底下的内容应该都可以创建,最后用 R.xxx.xxx 来引用。
如下就根据不同的类型,添加了不同的 app_name 字段,以及定义了 布尔值,可以通过 R.string.app_name 来引用。

注意,这里是添加,是在 string.xml 里面添加了一个字段app_name,所以在现有的 string.xml 中不能有这个字段,否则会报错!!!
productFlavors {
    dev {
        resValue "string", "app_name", "dev_myapp"
        resValue "bool", "isrRank", 'false'
    }
    stage {
        resValue "string", "app_name", "stage_myapp"
        resValue "bool", "isrRank", 'true'
    }
    prod {
        resValue "string", "app_name", "myapp"
        resValue "bool", "isrRank", 'true'
    }
}

通过以上我们大概可以推测出 color、dimen 也可以通过类似的方法添加。

<br />

3. 不同环境,动态修改指定的常量;

使用 BuildConfig 的变量。

①定义字段

当我们定义如下字段之后,编译后自动生成文件,在 app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig 目录,
打开这个文件,我们就能看到我们所定义的字段了。

productFlavors {
    dev {
        buildConfigField "String", "ENVIRONMENT", '"dev"'
    }
    stage {
        buildConfigField "String", "ENVIRONMENT", '"stage"'
    }
    prod {
        buildConfigField "String", "ENVIRONMENT", '"prod"'
    }
}
②引用字段

在我们自己的任意的类中,来直接通过 BuildConfig 就可以调用我们定义的字段。

public class Constants {
    public static final String ENVIRONMENT = BuildConfig.ENVIRONMENT;

}

注意:这里有个小细节,看其中第三个参数,是先用了“'”,然后在用了“"”,这种语法在 Java 里可能比较陌生,但是在很多其他语言中,这种用法是很常见的。
它的意思是 "dev" 这个整体是属于一个字符串,至于为什么要这么写,你把单引号去掉,然后去 app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig 这个文件看一看就知道了。

<br />

4. 不同环境,修改 AndroidManifest.xml 里渠道变量

①在 AndroidManifest.xml 里添加渠道变量
<application
    android:icon="${app_icon}"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    ...
    <meta-data
        android:name="UMENG_CHANNEL"
        android:value="${ENVIRONMENT}" />
    ...
</application>
②在 build.gradle 设置 productFlavors
productFlavors {
    dev {
        manifestPlaceholders = [ENVIRONMENT: "dev",
                                app_icon   : "@drawable/icon_dev"]
    }
    stage {
        manifestPlaceholders = [ENVIRONMENT: "stage",
                                app_icon   : "@drawable/icon_stage"]
    }
    prod {
        manifestPlaceholders = [ENVIRONMENT: "prod",
                                app_icon   : "@drawable/icon_prod"]
    }
}

这样我们可以在不同环境使用不同的 key 值。

<br />

5. 不同环境,引用不同的 module

这个就很强大了,根据不同的环境,引用对应的 module。
你可以替换大量的图片,string,color,vaule等等。

首先,要建立跟渠道对应的 module,然后再引用。
引用方式如下:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    // 引用本的项目
    devCompile project(':devModule')
    stageCompile project(':stageModule')
    prodCompile project(':prodModule')

    // 也可以分渠道引用网络的。因为这里都相同,所以地址也就都一样了
    devCompile 'com.roughike:bottom-bar:2.0.2'
    stageCompile 'com.roughike:bottom-bar:2.0.2'
    prodCompile 'com.roughike:bottom-bar:2.0.2'
}

xxxCompile 代表 各个渠道的名称。
然后把需要分渠道的文件,放到不同的 module 里面,把主项目的文件删掉。
千万注意:如果这样做了,每次需要引用的时候,在各个渠道的 module 里面都必须要放置文件哦,不然会找不到资源。
通过这种方式可以替换整套素材资源,具体如何使用还得看项目需求。

<br />
<br />
通过以上方式,我们基本可以 通过 gradle 动态设定应用标题,应用图标,替换常量,设置不同包名,更改渠道等等。


打包编译

最后,做完所有的配置之后,然后就是打包操作了。

打包某一个(日常编译)

因为 buildTypes 里面有两种,所以每个渠道都会有两种模式。

打包所有的,就是正常打包流程

如图所示:

图片来源网络 图片来源网络

<br />
打包完成之后,然后就可以在我们指定的目录下,看到我们所生成的apk包。


使用 local.properties 存放私密配置

以上就可以基本实现 gradle 的设置,但是如果我们要将我们的项目上传到 Github ,或者要将项目发送给别人。上面有些私密的东西就会被别人看到。比如:.jks 文件的密码。
在项目跟目录下,有个 local.properties 文件,我们可以使用它来存放一些私密的属性,然后在 gradle 中读取,而 local.properties 文件不需要上传。
local.properties 文件里设置如下:

sdk.dir=D\:\\Android\\android-sdk

gaodeKey=e348025dd034d1c347dd0345e34802

keyPassword=123456

在 build.gradle 读取 local.properties 字段信息

// 加载 local.properties 资源
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() ;
properties.load( inputStream )

android {
    ...

    // 签名文件
    signingConfigs {
        config {
            keyAlias 'lyl'
            // 获取 local.properties 字段信息
            keyPassword properties.getProperty( 'keyPassword' )
            storeFile file('../lyl.jks')
            storePassword properties.getProperty( 'keyPassword' )
        }
    }

    ...
}

这样就可以将自己想要隐藏的一些数据隐藏起来。


可能加快 Android Studio 编译的办法

1. 在根目录的 build.gradle 里加上如下代码:
allprojects {
    // 加上这个
    tasks.withType(JavaCompile) {
        //使在一个单独的守护进程编译
        options.fork = true
        //增量编译
        options.incremental = true
    }

    repositories {
        jcenter()
    }
}
2. 在 app 级别下 build.gradle 里 加上
android {
    dexOptions {
        incremental true
    }
}

最后放上一个多渠道的项目地址,可以参考:
https://github.com/Wing-Li/boon

OK。
如果本文有什么问题,请一定指出。
O(∩_∩)O

相关文章

网友评论

  • bb839216952f:请问多渠道打包生成不同包名的app, 在生成release版本的app的时候, jks文件用同一个还是用做一下区分?
    Wing_Li:@小坏孩 可以,都可以用同一个,包名都不同,签名一样也没用,一般每个产品有一个自己的。对将来,应该没有什么影响吧。
    bb839216952f:@Wing_Li 谢谢, 还有一个问题, 如果用同一个的话, 将来会有产生什么问题么? 另外一个公司开发的多个产品可以都用这一个签名么? 谢谢回答(已查了很多资料, 但没找到合适的答案)
    Wing_Li:可以用同一个,也可以做区分,在 release 里面有这么一行:signingConfig signingConfigs.config;这行代码也可以放到 各个渠道里面。
  • CROAD:楼主您好!您文章第5点,. "不同环境,引用不同的 module,各个渠道的 module 里面都必须要放置文件哦,不然会找不到资源。" 多渠道模块替换的花,每个渠道module都放置完整的一套资源文件吗,这样apk岂不是很大? 我有个需求是,不同渠道,启动页和部分界面的图片图标要更换该渠道的图片。但是图片不是很多,目前我是每次发包前,都是手动替换资源打包,非常麻烦。您有什么解决方案吗?
    Wing_Li:要是我的话,我就把图片全部放到项目里面,命名不同,然后通过代码逻辑判断当前APP属于哪个渠道,然后修改指定的图片。只需要在各个渠道内部写个常量标示当前渠道就可以了。这样就方便了。
  • wo叫天然呆:楼主有尝试过自定义打包任务么?就是不适用as带的打包任务,而是自己创建一个,在自己的打包任务中进行替换资源文件,替换字符串,替换包名等操作
    wo叫天然呆:@Wing_Li 是的,我公司的产品也是服务器动态编译,所以想跟你了解下这块技术
    Wing_Li:以后 Android 项目每次更新,在相关的配置文件有一些代码的行数是不能变的。用注释写上,哪行哪行不能变。不然服务器写好的脚本就得改。
    Wing_Li:代码写好之后放到指定的文件夹,用脚本语言 php 或者 python 控制修改某个路径下的某个文件 第n行,修改为 xxx。修改完之后,使用 gradle 命令打包。我们的项目就是这么做的。服务器动态编译。我知道,可以这样做,但是这套东西,不是我写的。
  • wmjwmj:请问怎么打包全部的app
    Wing_Li:最后的那个打包流程,到选择页面时,按住 Ctrl + A ,全选,然后确定。
  • Jiezhen_cjz:请问可以动态替换AndroidManifest.xml文件吗?该如何替换?
  • Answer_厮守:怎么根据不同渠道加载不同的代码?
  • b3596b7679a3:麻烦问一下,我在重新命名安装包时,提示有错误:
    release {
    // loadProperties("../buildConfig/debug.properties")
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

    // 批量打包
    applicationVariants.all { variant ->
    variant.outputs.each { output ->
    def outputFile = output.outputFile
    if (outputFile != null && outputFile.name.endsWith('.apk')) {
    //输出apk名称为:渠道名_版本名_时间.apk
    def fileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"
    // output.outputFile = new File(outputFile.parent, fileName)
    output.outputFile = new File("d:\\apk_build\\", fileName)
    }
    }
    }


    Cannot set the value of read-only property 'outputFile' for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=AdvancedDebug, filters=[]}} of type com.android.build.gradle.internal.api.ApkVariantOutputImpl.
  • Wing_Li:多渠道指定不同包名打包,不能实现多个 Apk 安装在同一设备,解决办法:http://ask.csdn.net/questions/662079
  • ff2db7a474c4:大神.为什么我换包名总事报错,那些自定义的view,在xml中总事报错,为啥呢
    Wing_Li:解决办法,记录一下:https://stackoverflow.com/questions/17571427/gradle-no-resource-identifier-found-for-attribute-when-using-flavors-and-packag
    ff2db7a474c4:@Wing_Li Error:(114) No resource identifier found for attribute 'position' in package '******' 总是这个错,您试过自定义view在xml里面的问题吗
    ff2db7a474c4:@Wing_Li 我换了包名就错,改回来就对了
  • ff6770e4d959:请问下楼主,我要修改签名打包后的apk文件的logo怎么修改啊,全部都是应用程序的logo的修改方案。。。急急急
    Wing_Li: @回不去的秋 我不会。但是,我怎么感觉你是在干什么坏事。该别人App的图标?
  • 430cbe953065:resValue "string", "app_name", "stage_boon"
    ** resValue "bool", "isrRank", 'true' ** // 动态修改 常量 字段
    请问下这个isrRank的具体使用可以举个栗子吗?
    430cbe953065:@Wing_Li :+1: 赞~~~谢☞!
    Wing_Li:boolean b = getResources().getBoolean(R.bool.isrRank);
  • 430cbe953065:学习!谢谢!
  • 张荣旗:我想问下,你这个都经过你自己测试了吗?为什么我的更换应用名更换不了
    Wing_Li:@zrq1060 测试过的。你是怎么写的呢?
  • d404cc63900e:你好,请教一下,现在我有很多张图片,需要循环替换icon,然后打包,那么多图片是必须要在res文件中吗?
  • 1c95e668b56d:太棒了,先Mark
  • 木猫尾巴:assembleRelease

    这种, buildConfigField 只能设置一次
  • 9fb0e6bbf299:项目中用到的图片能这么替换吗 就是某个页面的图标 不同包引用不同的图片资源
    CROAD:用代码控制要判断渠道,另外布局中资源文件,还有style文件的图片用代码控制不方便
    文淑:@Wing_Li 项目当中的图片,每次我都去替换,不是很麻烦。有没直接用配置文件解决的?
    Wing_Li:@bearli 项目当中的图片不需要这样替换啊,直接用代码控制不是更好。
  • 5d8bedc0017d:我想请教一下怎么利用gradle通过命令行的方式实现动态输入密码进行签名.
  • 将进酒_杯莫停:谢谢楼主,摸摸大! :grin:
    33af5afb733e:@Wing_Li
    <meta-data
    android:name="UMENG_CHANNEL"
    android:value="${UMENG_CHANNEL_VALUE}" />
    <application
    android:icon="${app_icon}">
    icon的图片还是manifestPlaceholders = [ app_icon:"@mipmap/icon" ]里的图片而不是manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi",
    app_icon : "@drawable/ic_launcher"] 里设置的图片,是我哪里写错啦吗?
    Wing_Li:@hswt 在 AndroidManifest.xml 中 android:icon="${app_icon}",大括号中,就相当于变量名。给它定义了一个变量名,然后在 build.gradle 中给这个变量赋值。
    manifestPlaceholders = [app_icon : "@drawable/icon_dev"]
    33af5afb733e:楼主你写的更换icon的方法可不可以稍微详细一点 按照你的方法 icon一直没有更换成功
  • 8cbdc85b5418:大牛,我试了下,包名没换成功吧,都不能装同时装,现在需求是批量生成app而且是不同app,就是能同时安装 :disappointed_relieved:
    Wing_Li:@混宅者 这个项目:https://github.com/Wing-Li/boon ,你把它下载到本地,重新做一个jks签名文件,把app目录build.gradle里15-20行,信息换掉。然后走本文最后的的打包流程。打包完成之后,apk是在最后一步指定的文件夹下的。需要自己把它全部手动安装到手机上。
  • 陆地蛟龙:第三方sdk 还有某些类的方法 能用这种方式配置吗?
  • 谢三弟:好像没有看到,分渠道改应用图标。
    谢三弟:@Wing_Li 谢谢😁没接触过,你这样一个例子我就明白了
    Wing_Li:@谢三弟 更新了一下文章,你可以试一试,有什么问题一定指出。 :smile:
    Wing_Li:@谢三弟 修改这个,用“4.修改 AndroidManifest.xml 里渠道变量”。跟这个的修改方式是一样的,AndroidManifest.xml 中 android:label="${app_name}" ,在 build.gradle 设置 productFlavors 依然是 manifestPlaceholders = [app_name: "dev", app_icon:"@drawable/icon_logo"] ,这个是数组,用逗号隔开。
  • 64a521bd73a3:楼主,能否实现打包时候添加权限的功能?
    简_单单: @Haidy 可以在不同的渠道选择使用不同的manifest 文件
    Wing_Li:@Haidy 这个还没有听说过.......而且,需求有点怪异,为啥要在打包的时候添加?之前添加好不就得了?之前不添加好APP也没法正常运行啊
  • 7289f01bc1bf:谢谢楼主,很有用。
  • hongjay:楼主我在和你做差不多的事,那么这样子打包后对开发测试来说是不是太慢了,每次编译都要好久,而studio自带的打包编译按钮要快很多啊。
    Wing_Li:@HongJay 实际开发的时候我都是把其他的都注释掉了,只剩了一个dev的其中一个。在有需要的时候,把其他再放开注释就好了。 感谢你的打赏 :smiley:
  • chuwe1:这有什么意义吗? 不同的渠道还要打不一样的包? 那岂不是不同的渠道就不能替换安装了?
    b95f67de1f50:@Wing_Li 谢了啊,,真的很实用,,我目前遇到的需求就是一个代码版本要多个包名和meta的值,本开始准备用shell脚本做了,,真是感谢楼主。
    Wing_Li:@chuwe1 是有意义的。其实,知道有这回事就可以了。觉得没意义可能是因为你现在没遇到,等以后遇到了,能想起来 gradle 有这么个功能就好了。而且,这个也并不是非用不可,也可以一个一个打包的,也能达到效果。
  • 8578a911ec40:不同的场景,更换报名,,报名换了,首先考虑绑定第三方的Key值不说,如果说我在不同的应用商店下载App下次升级报名不一样了,不是不会覆盖?有可能是不太明白楼主的意思,还望楼主解答一下
    Wing_Li:@8578a911ec40 比如说APP有两个环境。1.正式版,各大应用商店可以下载到的,包名肯定是一样的。2.开发版,在正式版APP已经上架了之后,我开发版也在同时开发2.0,这个开发版的包名跟正式版的包名是不一样的。
  • 小渚:楼主 这样多渠道打包 把包名也修改了 会不会影响里面升级的第三方key呢?比分享、登录、百度地图等
    Wing_Li:@e889269ee68b 如果是多个环境,应该就会对应多个报名和多个key
    e889269ee68b:@Wing_Li 那个第三方key不是绑定包名的么,,怎么不会影响呢
    Wing_Li:@小渚 不会影响,这跟单独打包应该是差不多一样的原理,一样的流程。只是一次修改,然后等就好了。就是把重复的工作,自动化了。
  • 妙法莲花1234:不错,请问你的qrscan是开源项目吗?可否提供下链接 :smile: 另外,类似miui相机的摄像头预览识别二维码是怎么实现的呢,可否提供下思路?谢谢啊 :grin:
    Wing_Li:@追风917 二维码用的最多的还是zxing。
    我猜测啊,其实扫描二维码的时候,已经调用了摄像头进行了拍摄,只是要多做一步,就是保存当前的图片。
    如果我做,我应该会研究一下zxing是怎么成像的,然后看能不能吧图片保存下来,再加个点击事件。
    妙法莲花1234: @Wing_Li 感谢😊哈哈
    Wing_Li:@追风917 明天写进一个项目来提供一下。
  • cfanr:不同包名的话,可以在debug的配置添加applicationIdSuffix的方式
    buildTypes {
    debug {
    applicationIdSuffix ".debug"
    }
    }
  • imknown:Win8 系统..
  • sendtion:看起来很厉害
  • 54577a59080c:您好,看了好久我还是没怎吗理解您的意思,如果按您这样替换,我的图片或者字符串替换很多都需要写到这个build。gradle文件中码
    d404cc63900e:@Wing_Li 那如果我有1000个icon 需要替换,都放在项目中吗?
    Wing_Li:@Wing_Li 不是不可以,是不适合这么做。笔误.....
    Wing_Li:@Sing_Ls 对,全部写进去,如果每次要替换大量的图片的话,应该是不可以的,难道要把所有的图片全部都放进apk包里面吗?
  • 捡淑:66666

本文标题:Gradle多渠道打包(动态设定App名称,应用图标,替换常量,

本文链接:https://www.haomeiwen.com/subject/kgwusttx.html