小明一个刚入行安卓的小萌新,刚刚在测试小姐姐那里交过学费(挨过骂)了解到软件开发过程中是需要区分正式、测试环境的。但是他稍加思考就能想到测试、正式环境的区别仅仅是host不一样而已,其他的比如接口名、参数名、返回的json格式均一模一样。于是他马上找到了解决方案,平时都用测试环境的,到上线的时候再换回正式环境不就可以了?在一次开发中需要请求三个不同接口,说干就干,于是小明就写出了以下代码,准备在上线时全局搜索www.test.com改成www.release.com,提交以后开开心心下班撩妹去了。
//请求接口1
NetWorkUtil.request("http://www.test.com?action=a1")
//请求接口2
NetWorkUtil.request("http://www.test.com?action=a2")
//请求接口3
NetWorkUtil.request("http://www.test.com?action=a3")复制代码
三天以后,产品经理跑过来说要加一个需求,新增了好几个接口,小明表示自己表现的机会到了,一股脑儿全部包下来了。可是做着做着发现有点不对劲啊,每次请求接口的时候都需要复制http://www.test.com这个域名,可是产品锦鲤追的紧啊,没办法先就这么办吧,实现了需求上线以后再说。
一周过去了,上线时间到了。测试小姐姐又跑过来问小明这个正式包的数据怎么不对,还是测试环境的?小明赶紧道歉,想起来了上线前需要修改域名这个事情,自己居然忘记了。心里一万只草泥马飞过,小明赶紧的匆匆忙忙地改完项目里所有接口请求的地方,这才松一口气,给测试小姐姐买了个奶茶打了个新包,这个版本终于成功上线了,还真是不容易啊。
一天以后,小明被项目经理叫到了办公室,把小明狠狠批了一顿。原来是小明有个地方忘记改了,线上用户的操作被记录到了测试数据库了。项目经理为了解决这个问题,将最近一天测试环境的该数据全部导入到了正式环境才解决,当然还收到了不少的投诉。不过还好这个数据不是核心数据,不是那么重要,不然小明的机票估摸着差不多就到手了。
小明痛定思痛,坚决要杜绝这种低级错误。于是他把需要改域名这个事情已经记录到备忘录里每天提醒了,除此之外,聪明的他还想到了一个办法,就是用一个全局的变量对域名进行保存,在上线前只需要切换一次就行了,类似于这种:
companion object {
const val HOST = "http://www.test.com"
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
setContentView(R.layout.activity_main)
//请求接口1
NetWorkUtil.request("$HOST?action=a1")
//请求接口2
NetWorkUtil.request("$HOST?action=a2")
//请求接口3 NetWorkUtil.request("$HOST?action=a3")
}复制代码
小明终于没有犯线上的低级错误了,但是后面业务需求越来越繁杂,服务端使用的域名也越来越多,并且很多第三方的api比如推送、bugly监测等也都需要切换id。每次上线需要修改一堆的域名和id。小明每天心态都跟高考一样,紧张又害怕,生怕自己再出问题,被强制送机票。而且就算在测试阶段,测试小姐姐偶尔也会让他打一个release包测试,虽然心里千万只草泥马不愿意,但是也没办法,先改成线上的域名吧,打完以后再改回来呗!于是小小明逐渐地熟悉了这一套切换方式,直到他看到了那一篇技术博客,小明他。。。哭了。
熟练开发者是怎么做的
小明看到的文章正是一篇关于测试和正式环境切换的技术文章,该文通俗易懂,还提供了完整的方案,小明看完觉得这不就是为自己准备的么,于是按照文章里的方式尝试了起来。文章中说到可以根据当前app是debug还是release来切换host,大概实现如下,首先在Application的onCreate()中获取到当前是否是debug模式,并且用静态变量进行记录,接下来需要区分测试、正式环境的时候就根据这个flag来判断即可。
class MyApplication: Application() {
companion object {
var IS_DEBUG = true
}
override fun onCreate() {
super.onCreate()
IS_DEBUG = (applicationInfo.flags != 0 && ApplicationInfo.FLAG_DEBUGGABLE != 0)
}
}复制代码
小明收到了该文章的启发,于是在项目所有需要区分测试、正式环境的地方都对上面的flag进行了判断,其代码大致如下:
companion object {
val HOST1 = if(MyApplication.IS_DEBUG)"http://www.test1.com" else "http://www.release1.com"
val HOST2 = if(MyApplication.IS_DEBUG)"http://www.test2.com" else "http://www.release2.com"
val HOST3 = if(MyApplication.IS_DEBUG)"http://www.test3.com" else "http://www.release3.com"
}复制代码
到了这里终于小明终于可以松口气不用设置备忘录,每次上线不用为了改域名问题而提心吊胆了,域名会智能地根据当前是debug包还是release包还自动赋值。但是后面加在这里的域名和第三方api越来越多,于是小明还在此基础上举一反三,进行了一波优化。小明了解到在系统打包的时候,如果在build.gradle文件中的buildTypes里添加debug和release的相应配置,系统在build/generated/source/buildConfig目录下会自动生成BuildConfig类,系统自动生成的类大概如下:
/**
* Automatically generated file. DO NOT MODIFY
*/
package xx.xx.xx;
public final class BuildConfig {
public static final boolean DEBUG = false;
public static final String APPLICATION_ID = "xx.xx.xx";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}
复制代码
这里面的字段是可以添加的,比如在build.gradle中设置好需要区分测试、正式环境的host,可以先按如下规则定义好
buildTypes {
debug {
buildConfigField "String", "URL", "\"www.test.com\""
}
release {
buildConfigField "String", "URL", "\"www.release.com\""
}
}
复制代码
则在编译的时候,系统会自动在BuildConfig中加入以下代码
public static final boolean DEBUG = true;
public static final String URL= "www.test.com";//如果是release包中会自动生成www.release.com
我们可以看到,在实际开发的时候根本不需要去设置当前是哪个域名,而是系统会自动来判断,从而在实际的业务需求开发时我们只需要使用BuildConfig.URL即可,小明将所有的域名以及第三方sdk需要的appkey都放到了buildTypes里,于是小明的代码可以改成这样子了:
//请求接口1
NetWorkUtil.request("${BuildConfig.URL}?action=a1")
//请求接口2
NetWorkUtil.request("${BuildConfig.URL}?action=a2")
//请求接口3
NetWorkUtil.request("${BuildConfig.URL}?action=a3")复制代码
当然这里URL最好封装到Common层中,这里就不多说,不是本文的重点。经过这一波的修改终于不用在每次上线时都修改URL了,而是系统会自动选择好URL,我们直接使用就可以了。