多个赛事的拆分打包,发布

PM 的需求

使用最新的版本1.1.2 如何实现高度自动化

1.名字 直播帝XXX ,XXX吧(有的平台不让叫直播)

羽毛球 乒乓球 网球 台球 排球

直播帝排球 我爱排球

2.版本号 1.0.0 内部版本号 100

3.渠道名 应用市场_专版类别首拼

4.包名 通用前缀.专版类别首拼

5.首页标题 = 应用名

6.主色调(tab) 、应用图标统一

7.发现内的图标

7.第一版本内容: 比赛 新闻 视频 发现(固定的)

PM这个需求主要是做ASO,主APP可以获得所有赛事的信息,子包需要将主包的功能分离,尽量复用主包的代码

主包的UI结构

  1. 底部的Tab采用RecyclerView来实现,其中Tab的资源文件是本地,这个图片每个专版的图标主色调不一样,
    所以RecyclerView的Adapter需要重写
  2. MainActivity布局:FrameLayout作为四个Tab功能的实现占位符,recyclerView的Item点击实现事件
    实现往FrameLayout里添加Fragment(第一次add,之后show,hide);

    主页结构

MainActivity Layout结构

采用TabLayout+ViewPager,ViewPager里的Fragment实现了懒加载

赛事结构

赛事 Layout结构

赛事页主要是一个RecyclerView

新闻结构

新闻 Layout结构

新闻页,TabLayout+ViewPager,外层ViewPager里的Fragment再嵌套一个TabLayout+ViewPager,其中允许外层的ViewPager侧滑禁止内层ViewPager,也就是在内层ViewPager滑动,带动是外层ViewPager侧滑,而不是内层

发现页

发现 Layout结构

发现页,采用一个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指定项目的源文件,可以把整个项目按照功能存放,为之后的模块化打下基础,同时也是提醒我们自己功能模块需要更好的分离