多个赛事的拆分打包,发布
PM 的需求
使用最新的版本1.1.2 如何实现高度自动化
1.名字 直播帝XXX ,XXX吧(有的平台不让叫直播)
羽毛球 乒乓球 网球 台球 排球
直播帝排球 我爱排球
2.版本号 1.0.0 内部版本号 100
3.渠道名 应用市场_专版类别首拼
4.包名 通用前缀.专版类别首拼
5.首页标题 = 应用名
6.主色调(tab) 、应用图标统一
7.发现内的图标
7.第一版本内容: 比赛 新闻 视频 发现(固定的)
PM这个需求主要是做ASO,主APP可以获得所有赛事的信息,子包需要将主包的功能分离,尽量复用主包的代码
主包的UI结构
底部的Tab采用RecyclerView来实现,其中Tab的资源文件是本地,这个图片每个专版的图标主色调不一样, 所以RecyclerView的Adapter需要重写
MainActivity布局:FrameLayout作为四个Tab功能的实现占位符,recyclerView的Item点击实现事件 实现往FrameLayout里添加Fragment(第一次add,之后show,hide);主页结构
采用TabLayout+ViewPager,ViewPager里的Fragment实现了懒加载
赛事结构
赛事页主要是一个RecyclerView
新闻结构
新闻页,TabLayout+ViewPager,外层ViewPager里的Fragment再嵌套一个TabLayout+ViewPager,其中允许外层的ViewPager侧滑禁止内层ViewPager,也就是在内层ViewPager滑动,带动是外层ViewPager侧滑,而不是内层
发现页
发现页,采用一个RecyclerView实现,图片是网络资源的。
赛事类详情页 赛事的详情的结构和新闻类似,只不过根据不同的类型创建了不懂Fragment模板,当然ViewPager里的Fragment都要懒加载
主包的功能大致介绍完了,在子包要改的也是这些
按功能抽离主包源文件 我们主要用到就是gradle android插件中的sourceSets这属性,他能够自定义Android工程的目录的结构
我们主要用到有java, res, manifest, 更多源文件目录设置,参见官网啦
首先我们需要把主包Tab的图片,App图标资源文件抽离出成两个单独的目录
res_color表示是color颜色主题的底部4个tab
在android添加如下sourceSets,Android Studio就会自动标识源文件目录啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apply plugin: 'com.android.application' android { ...... sourceSets { // Encapsulates configurations for the main source set. main { java.srcDirs = ['src/main/java'] //java文件目录 res.srcDirs = ["src/main/res", "src/main/res_main", "src/main/res_main_icon"] // 资源文件 manifest.srcFile('src/main/AndroidManifest.xml') // Manifest文件 } } ..... } .......
至此我们已经把主包的资源文件抽离完成了。把app/build.gradle 拷贝一份到app/gradle目录下,我们把他命名为normal.gradle
从命令行读取属性 从命令行中读取配置文件的路径,没有这个属性读取默认的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 // 从命令行中读取配置文件的路径,没有这个属性读取默认的路径 def loadProperties() { Properties p = new Properties() if (project.hasProperty("pro_path")) { File file = file(pro_path); p.load(new InputStreamReader(new FileInputStream(file), "utf-8")) return p; } else { File file = file("config/full.properties"); // 中文乱码问题 p.load(new InputStreamReader(new FileInputStream(file), "utf-8")) return p; } } // 从命令行中读取APP的名字,读取配置文件中的appName def appName() { if (project.hasProperty("app_name")) { return app_name } else { return loadProperties().getProperty("appName"); } } // 根据配置文件的theme字段,选取主题颜色 def appColor(String temp) { if (temp == "red") { return "#c01e2f" } else if (temp == "green") { return "#2fa51e" } else if (temp == "blue") { return "#0a4066" } else if (temp == "black") { return "#333333" } else { return "#1b82d2" } }
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #包名 applicationId=com.zhibodi.pingpangqiu #app名称 appName=乒乓球吧 cateid=47 cateName=乒乓球 twoid=4702 opentype=2 #简称,之后用到的替换字符 shortName=ppq #主题,之后用到的替换字符 theme=red #发现页资源路径 findIcon=res_find_icon # 友盟统计的key key=xxxx # 分享的key value weixinAppId=xxxxxxxxxxxxxxx weixinAppKey=xxxxxxxxxxxxxxxx weiboAppId=xxxxxxxxxxxxx weiboAppKey=xxxxxxxxxxxxxx qqAppId=xxxxxxxxxxxxxxx qqAppKey=xxxxxxxxxxxxxx # 广点通的广告 adAPPID=xxxxxxxxxxxxxxx adSplashPosID=xxxxxxxxxxxxx adBannerPosID=xxxxxxxxxxxx
最后的sourceSets
1 2 3 4 5 6 7 8 9 10 11 12 13 sourceSets { // Encapsulates configurations for the main source set. main { java.srcDirs = ['src/main/java', 'src/special/java'] res.srcDirs = ["src/main/res", "src/main/res_ad", "src/special/res_${p.shortName}_icon", "src/special/res", "src/special/${p.findIcon}", "src/main/res_${p.theme}"] manifest.srcFile('src/special/AndroidManifest.xml') } }
打包的命令
1 2 3 4 5 6 7 8 9 10 11 # !/usr/bin/env bash ./gradlew assembleRelease -P app_name="我爱排球" -P pro_path="config/pq.properties"&& ./gradlew assembleRelease -P app_name="排球吧" -P pro_path="config/pq.properties"&& ./gradlew assembleRelease -P app_name="直播播帝乒乓球" -P pro_path="config/ppq.properties"&& ./gradlew assembleRelease -P app_name="乒乓球吧" -P pro_path="config/ppq.properties"&& ./gradlew assembleRelease -P app_name="直播帝羽毛球" -P pro_path="config/ymq.properties"&& ./gradlew assembleRelease -P app_name="羽毛球吧" -P pro_path="config/ymq.properties"&& ./gradlew assembleRelease -P app_name="直播帝网球" -P pro_path="config/wq.properties"&& ./gradlew assembleRelease -P app_name="网球吧" -P pro_path="config/wq.properties"&& ./gradlew assembleRelease -P app_name="直播帝台球" -P pro_path="config/tq.properties"&& ./gradlew assembleRelease -P app_name="台球吧"-P pro_path="config/tq.properties"
“-P”即是需要从命令行读取的属性的后面接 key=value即可
例如读取cmd字段的value 在根目录下的build.gradle添加如下代码
1 2 3 4 5 6 task printProperties << { println propertiesFile if (project.hasProperty("cmd")){ println cmd } }
在命令行中执行1 ./gradlew printProperties -P cmd="哈哈哈哈"
就会打印出cmd的value
1 2 3 4 5 6 7 8 9 10 11 chengfangmingdeMacBook-Pro:zhibodinative chengfangming$ ./gradlew printProperties -P cmd="哈哈哈哈" Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Incremental java compilation is an incubating feature. :printProperties Hello from gradle.properties 哈哈哈哈 BUILD SUCCESSFUL Total time: 7.859 secs
可以在命令后继续加 -P key=value
根据自己的需求编写不一致的页面和逻辑 根据自己需求将主包java文件迁移到,子包的java源文件,这些java文件,绝大数的的业务逻辑与主包一致,只有少部分不一致,我们我新建一个java源文件的目录的原因是,不在主包的Java文件源文件中添写if else,从而达到不污染主包源文件,而是通过新建新的Java文件来实现,相同的功能,自己也少烧脑,不用也把自己绕晕了
总结 灵活的使用sourceSet指定项目的源文件,可以把整个项目按照功能存放,为之后的模块化打下基础,同时也是提醒我们自己功能模块需要更好的分离