CodeXiaoMai

CodeXiaoMai的博客


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

EnvironmentSwitcher

置顶 | 发表于 2019-07-29 更新于 2019-08-05 分类于 开源 阅读次数:
本文字数: 8.4k

Environment Switcher 是一个在 Android 的开发和测试阶段,运用 Java 注解、APT、反射、混淆等原理来一键切换环境的工具。

目前该项目已经达成如下成就:
GitHub starsGitHub forksGitHub watchers

它具有以下特点:

  • 配置简单
  • 安全,不泄漏测试环境地址
  • 不用重新打包即可一键切换环境
  • 支持按模块配置与切换环境
  • 支持环境切换通知回调
  • 自动生成 切换 保存 获取 环境的逻辑代码
  • 与项目解耦
  • ……

为什么不用 Gradle

看到这里你可能会想,这些功能我用 Gradle 就能搞定了,为什么要用 Environment Switcher 呢?别着急,下面我们来比较一下 Environment Switcher 和 Gradle。

比较内容 Environment Switcher Gradle Application Id 不同 Gradle Application Id 相同
运行时切换环境 ✔️ ✖️ ✖️
切换环境回调 ✔️ ✖️ ✖️
切换环境逻辑 自动生成 需要自己实现 需要自己实现
n 套环境打包数量 1个 n个 n个
多套环境同时安装 ✔️ ✔️ ✖️
支付等SDK包名校验 ✔️ ✖️ ✔️
多模块环境配置 ✔️ ✔️ ✔️
测试环境不泄露 ✔️ ✔️ ✔️
…… —— —— ——

这里就先列举这么多,仅 运行时切换环境 、打包数量、切换环境回调 这几个特点就比 Gradle 方便很多,而且 Environment Switcher 的接入成本也很低。是不是想试一试了?

使用方法

module environmentswitcher environmentswitcher-compiler environmentswitcher-compiler-release
version Download Download Download

配置项目的 build.gradle

java 版

1
2
3
4
5
6
dependencies {
...
implementation "com.xiaomai.environmentswitcher:environmentswitcher:$version"
debugAnnotationProcessor "com.xiaomai.environmentswitcher:environmentswitcher-compiler:$version"
releaseAnnotationProcessor "com.xiaomai.environmentswitcher:environmentswitcher-compiler-release:$version"
}

kotlin 版

1
2
3
4
5
6
7
8
apply plugin: 'kotlin-kapt'
...
dependencies {
...
implementation "com.xiaomai.environmentswitcher:environmentswitcher:$version"
kaptDebug "com.xiaomai.environmentswitcher:environmentswitcher-compiler:$version"
kaptRelease "com.xiaomai.environmentswitcher:environmentswitcher-compiler-release:$version"
}

编写 EnvironmentConfig 文件

这个类是 Environment Switcher 依赖的核心代码,所有获取、修改环境的逻辑代码都会依赖这个类中被 @Module 和 @Environment 两个注解标记的类和属性自动生成。

注意:如果你的项目中使用了 Kotlin,请使用 Java 语言编写 EnvironmentConfig,暂时还不支持使用 Kotlin 编写这个类。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 环境配置类</br>
*
* ⚠ 建议不要引用该类中的任何子类和成员变量,一但引用了非正式环境的属性,打包时混淆工具就不会移除该类,导致测试地址泄漏。</br>
* Environment Switcher 在编译 Release 版本时,会自动隐藏测试环境地址。</br></br>
*
* 建议将该类中所有被 {@link Module} 和 {@link Environment} 修饰的类或成员变量用 private 修饰,</br>
* Environment Switcher 会在编译期间自动生成相应的 Module_XX 和 Environment_XX 静态常量。</br>
* 例如:通过 EnvironmentSwitcher.MODULE_APP 就可以获取到 App 模块下相应的所有环境</br>
*/
public class EnvironmentConfig {

/**
* 整个 App 的环境
*/
@Module
private class App {
@Environment(url = "https://gank.io/api/", isRelease = true, alias = "正式")
private String online;
}

/**
* 特殊模块 Music 的环境
*/
@Module(alias = "音乐")
private class Music {
@Environment(url = "https://www.codexiaomai.top/api/", isRelease = true, alias = "正式")
private String online;

@Environment(url = "http://test.codexiaomai.top/api/", alias = "测试")
private String test;
}

/**
* 特殊模块 News 的环境
*/
@Module(alias = "新闻")
private class News {
@Environment(url = "http://news/release/", isRelease = true, alias = "正式")
private String release;

@Environment(url = "http://news/test/", alias = "测试")
private String test;

@Environment(url = "http://news/test1/")
private String test1;

@Environment(url = "http://news/sandbox/", alias = "沙箱")
private String sandbox;
}
}

@Module

被它修饰的类或接口表示一个模块,编译时会自动生成相应模块的 getXXEnvironment() 和 setXXEnvironment() 方法。一个被 @Module 修饰的类中,可以有 n (n>0) 个被 @Environment 修饰的属性,表示该模块中有 n 种环境。

例如:上面的代码中,有三个类被 @Module 修饰,意味着有三个模块,其中 App 模块中,只有一个属性被 @Environment 修饰,表示该模块只有一种环境;而 Music 和 News 模块分别有 2 种和 4 种环境。

此外 @Module 还有一个可选属性 alias ,用来指定该模块的别名。该值默认为空字符串。这个属性的主要目的是在切换环境 UI 页面显示中文名称。例如:Music 和 News 模块在切换环境页面中就会分别显示 “音乐” 和 “新闻”。

注:如果你的项目中所有模块共用同一个 Host 地址,那么只需配置一个 Module 就可以了。

@Environment

被它修饰的属性表示一个环境,必须指定 url 的值,此外还有两个可选属性:isRelease 和 alias。

  • isRelease 是一个 boolean 型的属性,默认为 false,当值为 true 时,它就是所在 Module 的默认环境,以及 App 正式发布时的环境。一个 Module 中必须有且只有一个 Environment 的 isRelease 的值为 true,否则编译会失败。

    例如:Music 模块中有两种环境分别是 online(正式)和 test (测试),因为 online 的 isRelease = true,所以它就是默认环境和App 正式发布时的环境。

  • alias 和 @Module 中的 alias 相似,用于在切换环境的UI页面展示该环境的名字,该值默认为空字符串,如果给它指定非空字符串,则环境的名字就被指定为 alias 的值。

再次强调:一个 Module 中必须有且只有一个 Environment 的 isRelease 的值为 true,否则编译会失败。

点击菜单栏中的 “Build” -> “Rebuild Project”,等待编译完成。

到这里整个配置就算完成了,接下来就可以在项目中愉快的获取相应模块的环境地址了。

添加入口

手动切换环境当然要有一个页面,这个页面 Environment Switcher 已经自动集成了,只需要添加一个入口跳转即可(这个入口只在 Debug 测试等内部版显示)。

例如:在“我的”页面中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
if (!BuildConfig.DEBUG) {
// only show in debug
findViewById(R.id.bt_switch_environment).setVisibility(View.GONE);
return;
}

findViewById(R.id.bt_switch_environment).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// entrance of switch environment
EnvironmentSwitchActivity.launch(getContext());
}
});
}

你可以使用 Environment Switcher 已经提供的 EnvironmentSwitchActivity.launch(getContext()) 方法启动;当然你也可以通过 startActivity(new Intent(getContext(), EnvironmentSwitchActivity.class)) 启动,看个人喜好了。

获取相应模块的环境地址:

1
2
3
String appEnvironment = EnvironmentSwitcher.getAppEnvironment(this, BuildConfig.DEBUG);
String musicEnvironment = EnvironmentSwitcher.getMusicEnvironment(this, BuildConfig.DEBUG);
String newsEnvironment = EnvironmentSwitcher.getNewsEnvironment(this, BuildConfig.DEBUG);

获取相应模块的环境实体类(since 1.4):

1
2
3
EnvironmentBean appEnvironmentBean = EnvironmentSwitcher.getAppEnvironmentBean(this, BuildConfig.DEBUG);
EnvironmentBean musicEnvironmentBean = EnvironmentSwitcher.getMusicEnvironmentBean(this, BuildConfig.DEBUG);
EnvironmentBean newsEnvironmentBean = EnvironmentSwitcher.getNewsEnvironmentBean(this, BuildConfig.DEBUG);

这里需要注意的是获取相应模块的地址需要两个参数。

第一个就是一个 Context 不用解释,因为 Environment Switcher 是用 SharedPreferences 进行存储数据的。

第二个参数是一个 boolean 型的值,如果为 true 表示当前为 Debug 或测试等内部使用版本,此时获取到的地址是我们手动切换保存的地址;而如果为 false 表示当前为要发布给用户使用的版本,此时获取到的地址为我们在 @Environment 中指定 isRelease = true 的地址,手动切换的环境地址不再生效。

添加监听事件

Environment Switcher 支持切换环境回调,你可以通过以下方法添加,需要注意的是不要忘记在不需要监听环境切换事件时移除监听事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends AppCompatActivity implements OnEnvironmentChangeListener{

private static final String TAG = "MainActivity";

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 添加监听事件
EnvironmentSwitcher.addOnEnvironmentChangeListener(this);
}

@Override
public void onEnvironmentChanged(ModuleBean module, EnvironmentBean oldEnvironment, EnvironmentBean newEnvironment) {
Log.e(TAG, module.getName() + "由" + oldEnvironment.getName() + "环境,Url=" + oldEnvironment.getUrl()
+ ",切换为" + newEnvironment.getName() + "环境,Url=" + newEnvironment.getUrl());
}

@Override
protected void onDestroy() {
super.onDestroy();
// 移除监听事件
EnvironmentSwitcher.removeOnEnvironmentChangeListener(this);
}
}

切换SDK开发环境

我们在项目中一般会依赖第三方提供的SDK,而且这些SDK也会提供测试环境,如果要在App内切换环境,使用上面的方法就不行了。那该怎么办呢?

例如我们的“直播”模块是引用的SDK,我们可以这样做:

  1. 首先在 EnvironmentConfig.java 中配置”直播”模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class EnvironmentConfig {
    @Module(alias = "直播")
    private class Live {
    @Environment(url = "online", isRelease = true, alias = "正式")
    private String online;

    @Environment(url = "test", alias = "测试")
    private String test;
    }
    }

    url 在这里只是用来区分环境,不用为真实的 url,但要保证同一模块中每个环境的 url 不同。

  2. 在 Application 中添加监听

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    EnvironmentSwitcher.addOnEnvironmentChangeListener(new OnEnvironmentChangeListener() {
    @Override
    public void onEnvironmentChanged(ModuleBean module, EnvironmentBean oldEnvironment, EnvironmentBean newEnvironment) {
    if (module.equals(EnvironmentSwitcher.MODULE_LIVE)) {
    if (newEnvironment.equals(EnvironmentSwitcher.LIVE_ONLINE_ENVIRONMENT)) {
    // 调用 SDK 切换环境的方法,正式环境
    } else if (newEnvironment.equals(EnvironmentSwitcher.LIVE_TEST_ENVIRONMENT)) {
    // 调用 SDK 切换环境的方法,测试环境
    }
    }
    }
    });

    利用 Environment Switcher 的环境切换回调,实现切换 SDK 环境。

好了,关于Environment Switcher 的介绍就到此为止吧,更多使用介绍可参考Demo,Demo 中有 Environment Switcher 结合 Retrofit 使用的详细实现过程。

Environment Switcher

# 开源 # EnvironmentSwitcher
ConcurrentModificationException 异常分析与解决方法
字符编码的历史演变
  • 文章目录
  • 站点概览
CodeXiaoMai

CodeXiaoMai

CodeXiaoMai的博客
12 日志
6 分类
19 标签
GitHub E-Mail
  1. 1. 为什么不用 Gradle
  2. 2. 使用方法
    1. 2.1. 配置项目的 build.gradle
      1. 2.1.1. java 版
      2. 2.1.2. kotlin 版
    2. 2.2. 编写 EnvironmentConfig 文件
      1. 2.2.1. @Module
      2. 2.2.2. @Environment
    3. 2.3. 点击菜单栏中的 “Build” -> “Rebuild Project”,等待编译完成。
    4. 2.4. 添加入口
    5. 2.5. 获取相应模块的环境地址:
    6. 2.6. 获取相应模块的环境实体类(since 1.4):
    7. 2.7. 添加监听事件
    8. 2.8. 切换SDK开发环境
© 2019 CodeXiaoMai
|