開發時經常出現內外網切換的情況,比如測試人員測試應用的時候,需要先在內網測試一遍,然後在外網再測試一遍,如果外網有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,必須加上這個判斷,不然應用有閃退的危險。