开发时经常出现内外网切换的情况,比如测试人员测试应用的时候,需要先在内网测试一遍,然后在外网再测试一遍,如果外网有bug的话,就需要应用切回内网进行调试。这个时候一般的处理方式就是不断的打包,其实我们可以在开发的时候给应用加上一个修改接口服务器地址的隐藏功能(一定要是隐藏功能),这样就会方便很多了。
1、初始接口地址
初始的接口地址我们放在MyApplication
中写入sp持久化保存,然后在联网是从sp中读取
public class MyApplication extends LitePalApplication {
private static MyApplication instance;
private SharedPreferences sp;
private SQLiteDatabase database;
public String BASE_URL = "http://192.168.100.152:8282/cableNetworkIntf/"; //本地接口
@Override
public void onCreate() {
super.onCreate();
instance = this;
LitePal.initialize(this);
//建表
database = Connector.getDatabase();
sp = getSharedPreferences("user", MODE_PRIVATE);
//初始化服务器信息
initServerUrl();
}
private void initServerUrl() {
if (TextUtils.isEmpty(getBaseURL()))setBaseURL(BASE_URL);
...
}
//-----------------服务器信息---------------
public String getBaseURL() {
return sp.getString("BaseURL", "");
}
public void setBaseURL(String baseURL) {
sp.edit().putString("BaseURL", baseURL).commit();
}
...
}
user.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
...
<string name="BaseURL">http://192.168.100.152:8282/cableNetworkIntf/</string>
...
</map>
2、接口地址的使用
初始化接口地址保存之后,我们要在联网框架中使用
public class HttpMethods {
private static final int DEFAULT_TIMEOUT = 15;
public NetworkService networkService;
private String username = "$";
private String password = "$";
// basic认证信息
String credentials = username + ":" + password;
final String basic = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
private static HttpMethods httpMethodsods;
//构造方法私有
private HttpMethods() {
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(InterceptorUtils.HeaderInterceptor(basic))
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
//防止修改的url不合理
try {
networkService = new Retrofit.Builder()
.baseUrl(MyApplication.getInstance().getBaseURL())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(httpClient)
.build()
.create(NetworkService.class);
}catch (Exception e){
e.printStackTrace();
}
}
//修改接口地址后重置baseUrl
public static void resetBaseUrl(){
httpMethodsods = new HttpMethods();
}
public static HttpMethods getInstance() {
if (httpMethodsods==null){
httpMethodsods = new HttpMethods();
}
return httpMethodsods;
}
private void toSubscribe(Observable o, Subscriber s) {
o.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(s);
}
//----------请求方法------------
//登录
public void login(Subscriber<HttpResult.LoginResponse> subscriber, Map<String, String> options) {
Observable observable = networkService.login(options);
toSubscribe(observable, subscriber);
}
...
}
3、登录界面隐藏修改入口
在登录界面的logo处隐藏进入修改接口地址的入口,需要连点5下进入修改地址界面
@OnClick({R.id.app_logo, R.id.btn_login})
public void onClick(View view) {
switch (view.getId()) {
case R.id.app_logo:
ShowServerSetting();
break;
case R.id.btn_login:
loginHttp();
break;
}
}
...
/**
* 去往接口服务器设置
*/
private long[] mHits = new long[5];
private void ShowServerSetting() {
System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);//移动数据
mHits[mHits.length - 1] = SystemClock.uptimeMillis();//新点击时获取的当前时间存到数组的最后一个
if (mHits[0] >= (mHits[mHits.length - 1] - 20000)) {//第一次和最后一次点击的时间间隔小于20000ms(20s),判定为有效的5次点击
Intent intent = new Intent(LoginActivity.this, AppServerActivity.class);
intent.putExtra(MConstant.serverSet, MConstant.appServer);
startActivity(intent);
}
}
@Override
protected void onResume() {
super.onResume();
mHits = new long[5]; //重新进入页面时清空之前点击记录
}
4、修改接口地址界面
private long clickTime = 0;
private boolean isEdit;
@OnClick({R.id.header_left_img, R.id.header_right_tv})
public void onClick(View view) {
switch (view.getId()) {
case R.id.header_left_img:
finish();
break;
case R.id.header_right_tv:
if (System.currentTimeMillis() - clickTime < 1000) return;
clickTime = System.currentTimeMillis();
isEdit = !isEdit;
if (isEdit) {
headerRightTv.setText("保存");
if (serverUrl0 != null) MyUtils.setEditTextEditable(serverUrl0, true);
if (serverUrl1 != null) MyUtils.setEditTextEditable(serverUrl1, true);
} else {
final String trim = serverUrl0.getText().toString().trim();
String end = "";
if (!TextUtils.isEmpty(trim)) {
end = trim.substring(trim.length() - 1, trim.length());
}
if (MyUtils.isUrl(trim)&&"/".equals(end)) {
instance.setBaseURL(trim);
HttpMethods.resetBaseUrl();
} else {
new CenterHintToast(AppServerActivity.this, "请输入有效的URL,并以/结尾");
return;
}
}
}
涉及到的工具类
/**
* 设置edittext是否可编辑
* @param editText
* @param value
*/
public static void setEditTextEditable(EditText editText, boolean value){
if (value) {
editText.setFocusableInTouchMode(true);
editText.requestFocus();
editText.setGravity(Gravity.LEFT);
}else {
editText.setFocusableInTouchMode(false);
editText.clearFocus();
editText.setGravity(Gravity.LEFT);
}
}
...
public class CenterHintToast extends Toast {
public CenterHintToast(Context context, String hintMsg) {
super(context);
// Toast.makeText(context, hintMsg, Toast.LENGTH_SHORT).show();
Toast toast = Toast.makeText(context, hintMsg, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER,0,0);
toast.show();
}
}
...
/**
* 匹配URL地址
*/
public static boolean isUrl(String str) {
return match(str, "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");
}
/**
* 正则表达式匹配
* @param text 待匹配的文本
* @param reg 正则表达式
* @return
*/
private static boolean match(String text, String reg) {
if (TextUtils.isEmpty(text) || TextUtils.isEmpty(reg)) return false;
return Pattern.compile(reg).matcher(text).matches();
}
注意上述代码中的"/".equals(end)
判断,如果你修改后的接口地址不是以 / 结尾的话,2、接口地址使用
中的NetworkService
将无法创建,这也是我为什么加上异常捕获的原因。
5、使用新的接口地址登录
修改接口地址并保存之后,回到登录界面即可使用新的接口地址进行登录了(即时生效)。
private void loginHttp() {
if (!MyUtils.isNetWorkConnected(this)){
new CenterHintToast(LoginActivity.this, "请检查网络!");
return;
}
String user = userLogin.getText().toString().trim();
String psw = pswLogin.getText().toString().trim();
if (TextUtils.isEmpty(user) || TextUtils.isEmpty(psw)) {
new CenterHintToast(LoginActivity.this, "请输入用户名和密码!");
return;
}
final long millis = System.currentTimeMillis();//时间戳,作为当前用户登录的唯一标识
Map<String, String> map = new HashMap<>();
map.put("userName", user);
map.put("passWord", psw);
map.put("token", millis + "");
HttpMethods instance = HttpMethods.getInstance();
if (instance==null||instance.networkService==null){
new CenterHintToast(this,"接口地址无效,请重新输入");
return;
}
instance.login(new Subscriber<HttpResult.LoginResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
new CenterHintToast(LoginActivity.this,"登录失败,稍后重试!");
}
@Override
public void onNext(HttpResult.LoginResponse response) {
if ("0".equals(response.code)) {
UserBean bean = response.user;
MyApplication instance = MyApplication.getInstance();
instance.setUserInfo(bean);
instance.setTokenTIme(millis+"");
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
} else {
new CenterHintToast(LoginActivity.this, response.msg);
}
}
}, map);
}
注意登录的时候我加上的if (instance==null||instance.networkService==null)
的判断,因为修改地址之后instance和instance.networkService都可能为null,必须加上这个判断,不然应用有闪退的危险。