Android開發(fā)工具之環(huán)境切換
背景介紹
該工具是續(xù)篇,上一篇介紹的是Http日志抓取工具,這一篇講解環(huán)境切換,環(huán)境切換為什么需要這個(gè),現(xiàn)在稍微大點(diǎn)的公司基本都是兩套以上環(huán)境,分為測試、預(yù)發(fā)和線上,有些公司沒有預(yù)發(fā)這個(gè)部分。原來開發(fā)hybrid App的時(shí)候,可以直接修改配置文件的一個(gè)配置值可以切換當(dāng)前環(huán)境,但是App如何這么設(shè)置的劃,開發(fā)比較耗費(fèi)時(shí)間,每次調(diào)試測試或者預(yù)發(fā)環(huán)境的接口時(shí),總是得重新打包,對于一個(gè)開發(fā)者來說是十分痛苦的。
所以,環(huán)境切換就孕育而生,我們就打一次debug包,然后需要調(diào)試測試環(huán)境接口時(shí),就搖一搖進(jìn)入調(diào)試界面,然后點(diǎn)擊切換環(huán)境,切換成測試環(huán)境,然后自動(dòng)重啟app,再次打開之后,app就進(jìn)入測試環(huán)境,接口切換為測試環(huán)境的接口,數(shù)據(jù)配置都自動(dòng)切換成測試環(huán)境的東西,無需打包,無需修改代碼、無需重新運(yùn)行。
頁面截圖
[站外圖片上傳中...(image-d2f574-1569233613624)]
上面就是Debug界面,這個(gè)跟上一篇介紹的都是放在BaseActivity中,全局搖一搖就可以顯示該界面,點(diǎn)擊當(dāng)前環(huán)境就可以選擇當(dāng)前環(huán)境。
代碼實(shí)現(xiàn)
其實(shí)這部分也很簡單,主要是通過Sp存儲當(dāng)前環(huán)境狀態(tài),默認(rèn)第一次是測試環(huán)境,然后每次切換環(huán)境之后,將新的環(huán)境狀態(tài)存儲到sp當(dāng)中,然后清除App的存儲的數(shù)據(jù)狀態(tài),或者將數(shù)據(jù)存儲到每個(gè)狀態(tài)對應(yīng)的目錄下,這里見仁見智,退出登錄并重新啟動(dòng)App,讓App的新環(huán)境生效,重新加載新的App。
首先創(chuàng)建一個(gè)DebugActivity,來處理App所有的調(diào)試界面
DebugActivity.java
/**
* Created by mguo on 2018/5/3.
*/
@Route(path = RouterConstant.ACTIVITY_DEBUG)
public class DebugActivity extends BaseSwipeBackActivity {
@Bind(R.id.tv_env)
TextView tvEnv;
@Bind(R.id.tv_device_bind)
TextView tvDeviceBind;
@Bind(R.id.rl_device_bind)
RelativeLayout rlDeviceBind;
String[] items = new String[]{"測試", "生產(chǎn)", "預(yù)生產(chǎn)", "本地"};
String[] itemBind = new String[]{"開啟校驗(yàn)", "取消校驗(yàn)"};
@Bind(R.id.img_back)
ImageView imgBack;
@Bind(R.id.index_app_name)
TextView indexAppName;
@Bind(R.id.tv_title_right)
TextView tvTitleRight;
@Bind(R.id.rl_wechat_share)
RelativeLayout rlWechatShare;
@Override
protected int getLayoutId() {
return R.layout.activity_debug_layout;
}
@Override
protected void onInitialization(Bundle bundle) {
ARouter.getInstance().inject(this);
}
@Override
protected void initView(Bundle bundle) {
if (GlobalEnv.isRelease()) {
rlDeviceBind.setVisibility(View.GONE);
}
rlDeviceBind.setVisibility(View.VISIBLE);
tvEnv.setText(items[GlobalEnv.getEnvModel()]);
tvDeviceBind.setText(itemBind[GlobalEnv.isVerifyDevice() ? 0 : 1]);
indexAppName.setText("環(huán)境調(diào)試");
tvTitleRight.setText(getString(R.string.string_txt_cancel));
tvTitleRight.setVisibility(View.VISIBLE);
}
@Override
protected void initData(Bundle bundle) {
}
@OnClick({R.id.rl_cd,R.id.rl_http,R.id.layout_debug, R.id.rl_demo_list, R.id.layout_webview, R.id.rl_device_bind, R.id.tv_title_right, R.id.img_back, R.id.rl_update, R.id.rn_setting,R.id.rl_wechat_share})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.img_back:
finish();
break;
case R.id.rl_demo_list:
gStartActivity(DemoListActivity.class);
break;
case R.id.tv_title_right:
finish();
break;
case R.id.rl_device_bind:
AlertDialog.Builder builder1 = new AlertDialog.Builder(this);
builder1.setTitle("選擇是否校驗(yàn)");
builder1.setSingleChoiceItems(itemBind, GlobalEnv.getEnvModel(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.dismiss();
GlobalEnv.setVerifyDevice(getContext(), i == 0);
tvDeviceBind.setText(itemBind[i]);
}
});
builder1.show();
break;
case R.id.layout_debug:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("選擇環(huán)境");
builder.setSingleChoiceItems(items, GlobalEnv.getEnvModel(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.dismiss();
if (GlobalEnv.getEnvModel() == i) {
return;
}
showToast("應(yīng)用重啟中");
GlobalEnv.setEnvModel(i);
tvEnv.setText(items[i]);
UserManager.getInstance().logout();
AppExitUtil.restart(DebugActivity.this);
}
});
builder.show();
break;
case R.id.layout_webview:
getRouter().showWebviewWithUrl("file:///android_asset/index.html");
break;
case R.id.rn_setting:
gStartActivity(RNDebugActivity.class);
// down();
break;
case R.id.rl_cd:
openBrowser("");
break;
case R.id.rl_http:
gStartActivity(HttpListActivity.class);
break;
case R.id.rl_update:
new SweetAlertDialog.Builder(this).setMessage("是否打開瀏覽器").setPositiveButton("確定", new SweetAlertDialog.OnDialogClickListener() {
@Override
public void onClick(Dialog dialog, int which) {
String url = "";
int envModel = GlobalEnv.getEnvModel();
switch (envModel) {
case ENV_DEBUG:
url = "";
??????????? break;
case ENV_RELEASE:
url = "";
break;
case ENV_BETA:
url = "";
break;
}
openBrowser(url);
}
}
).setNegativeButton("取消", null)
.show();
break;
case R.id.rl_wechat_share:
gStartActivity(SendToWXActivity.class);
break;
}
}
@Override
protected String initPageName() {
return "調(diào)試頁面";
}
}
GlobalEnv.java
public class GlobalEnv {
public static final int ENV_DEBUG = 0;//測試環(huán)境
public static final int ENV_RELEASE = 1;//生產(chǎn)環(huán)境
public static final int ENV_BETA = 2;//預(yù)生產(chǎn)環(huán)境
public static final int ENV_LOCAL = 3;//本地環(huán)境
private static boolean mVerifyDevice = true;
private static int mEnvModel = 0;
private static String mtaChannel = "cstore";
private static String versionName = "";
public static String getChannel() {
return mtaChannel;
}
public static String getVersionName() {
return versionName;
}
public static boolean isDebug() {
return TextUtils.equals(BuildConfig.BUILD_TYPE, "debug");
}
public static boolean isBeta() {
return TextUtils.equals(BuildConfig.BUILD_TYPE, "beta");
}
public static boolean isRelease() {
return TextUtils.equals(BuildConfig.BUILD_TYPE, "release");
}
public static int getEnvModel() {
return mEnvModel;
}
public static void setVerifyDevice(Context context, boolean verify) {
mVerifyDevice = verify;
SharedUtil.instance(context).saveBoolean("env_device_verify", verify);
}
public static boolean isVerifyDevice() {
return mVerifyDevice;
}
public static void setEnvModel(int envModel) {
mEnvModel = envModel;
SharedUtil.instance(BaseApp.getContext()).saveInt("mEnvModel", envModel);
switch (envModel) {
case ENV_DEBUG:
ApiManager.getInstance().setBaseUrl(ApiConstant.BASETESTURL);
ApiManager.getInstance().setWebUrl(ApiConstant.WEBTESTURL);
ApiManager.getInstance().setTaskUrl(ApiConstant.TASKTESTURL);
break;
case ENV_RELEASE:
ApiManager.getInstance().setBaseUrl(ApiConstant.BASEURL);
ApiManager.getInstance().setWebUrl(ApiConstant.WEBURL);
ApiManager.getInstance().setTaskUrl(ApiConstant.TASKURL);
ApiManager.getInstance().setEmosUrl(ApiConstant.EOMSURL);
break;
case ENV_BETA:
ApiManager.getInstance().setBaseUrl(ApiConstant.BASESTAGEURL);
ApiManager.getInstance().setWebUrl(ApiConstant.WEBSTAGEURL);
ApiManager.getInstance().setTaskUrl(ApiConstant.TASKSTAGEURL);
ApiManager.getInstance().setEmosUrl(ApiConstant.EOMSURL);
break;
case ENV_LOCAL:
ApiManager.getInstance().setBaseUrl(ApiConstant.BASELOCALURL);
ApiManager.getInstance().setWebUrl(ApiConstant.WEBLOCALURL);
ApiManager.getInstance().setTaskUrl(ApiConstant.TASKLOCALURL);
break;
default:
ApiManager.getInstance().setBaseUrl(ApiConstant.BASETESTURL);
ApiManager.getInstance().setWebUrl(ApiConstant.WEBTESTURL);
ApiManager.getInstance().setTaskUrl(ApiConstant.TASKTESTURL);
break;
}
}
public static void init(Application application) {
mVerifyDevice = SharedUtil.instance(application).getBoolean("env_device_verify", true);
mtaChannel = DeviceUtil.getAppMetaData(application, "GJ_CHANNEL");
versionName = DeviceUtil.getVersionName(application);
Log.v("GlobalEnv", "channel=" + mtaChannel);
// if (GlobalEnv.isRelease()) {
// GlobalEnv.setEnvModel(GlobalEnv.ENV_RELEASE);
// return;
// }
if (ApiManager.getInstance().isSetting()) {
mEnvModel = SharedUtil.instance(application).getInt("mEnvModel", 0);
return;
}
if (TextUtils.equals(BuildConfig.BUILD_TYPE, "debug")) {
setEnvModel(ENV_DEBUG);
} else if (TextUtils.equals(BuildConfig.BUILD_TYPE, "release")) {
setEnvModel(ENV_RELEASE);
} else if (TextUtils.equals(BuildConfig.BUILD_TYPE, "beta")) {
setEnvModel(ENV_BETA);
}
}
}
主要操作就是,切換環(huán)境-->保存新的環(huán)境配置-->退出登陸,并清除數(shù)據(jù)-->重啟App,里面有一部分操作是個(gè)人項(xiàng)目里面將網(wǎng)絡(luò)基礎(chǔ)BaseUrl和webview需要設(shè)置的環(huán)境配置的url進(jìn)行重置。
舉例: Http配置Url
// 其他統(tǒng)一的配置
GHttp.getInstance().init(context) //必須調(diào)用初始化
.setBaseUrl(ApiManager.getInstance().getBaseUrl())
.setOkHttpClient(builder.build()) //建議設(shè)置OkHttpClient,不設(shè)置會使用默認(rèn)的
.setCacheMode(CacheMode.NO_CACHE) //全局統(tǒng)一緩存模式,默認(rèn)不使用緩存,可以不傳
// .setCacheTime(CacheEntity.CACHE_NEVER_EXPIRE) //全局統(tǒng)一緩存時(shí)間,默認(rèn)永不過期,可以不傳
.setCacheTime(1000 * 60 * 60 * 24) //全局統(tǒng)一緩存時(shí)間,默認(rèn)永不過期,可以不傳
.setRetryCount(3) //全局統(tǒng)一超時(shí)重連次數(shù),默認(rèn)為三次,那么最差的情況會請求4次(一次原始請求,三次重連請求),不需要可以設(shè)置為0
.addCommonHeaders(headers) //全局公共頭
.addCommonParams(params); //全局公共參數(shù)
Url配置管理類ApiManager.java
public class ApiManager {
private static ApiManager apiManager;
private Context mContext;
public ApiManager(Context context) {
mContext = context;
}
public static ApiManager getInstance() {
if (apiManager == null) {
synchronized (ApiManager.class) {
if (apiManager == null) {
apiManager = new ApiManager(BaseApp.getContext());
}
}
}
return apiManager;
}
private String baseUrl;
private String webUrl;
private String taskUrl;
private String emosUrl;
public boolean isSetting() {
return !TextUtils.isEmpty(SharedUtil.instance(mContext).getString("baseUrl"));
}
public String getBaseUrl() {
return TextUtils.isEmpty(baseUrl) ? SharedUtil.instance(mContext).getString("baseUrl", ApiConstant.BASEURL) : baseUrl;
}
public String getWebURL() {
return TextUtils.isEmpty(webUrl) ? SharedUtil.instance(mContext).getString("webUrl", ApiConstant.WEBURL) : webUrl;
}
public String getTaskUrl() {
return TextUtils.isEmpty(taskUrl) ? SharedUtil.instance(mContext).getString("taskUrl", ApiConstant.TASKURL) : taskUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
SharedUtil.instance(mContext).saveString("baseUrl", baseUrl);
}
public void setTaskUrl(String taskUrl) {
this.taskUrl = taskUrl;
SharedUtil.instance(mContext).saveString("taskUrl", taskUrl);
}
public void setWebUrl(String webUrl) {
this.webUrl = webUrl;
SharedUtil.instance(mContext).saveString("webUrl", webUrl);
}
public String getEmosUrl() {
// return emosUrl;
return TextUtils.isEmpty(emosUrl) ? SharedUtil.instance(mContext).getString("emosUrl", ApiConstant.EOMSURL) : emosUrl;
}
public void setEmosUrl(String emosUrl) {
this.emosUrl = emosUrl;
SharedUtil.instance(mContext).saveString("emosUrl", emosUrl);
}
}
這只是部分環(huán)境代碼,剩下的邏輯都可以這樣來處理,switch一下環(huán)境,然后處理相應(yīng)的邏輯就OK,環(huán)境切換主要是為了滿足公司測試、預(yù)發(fā)和線上環(huán)境的切換,比如測試測完測試環(huán)境,然后服務(wù)端發(fā)布了預(yù)發(fā)環(huán)境,直接搖一搖切換環(huán)境,直接環(huán)境就切換成了預(yù)發(fā)環(huán)境的接口鏈接,線上也是這種原理,然后就可以直接進(jìn)行測試。
此篇over。