今天做了一个功能,设置一个心跳操作,根据接口返回时间再次请求接口,没响应或者弱网等异常情况,自动再次请求
由于要做一个SDK,不需要引用第三方包,所以用了CountDownTimer 倒计时和 AsyncTask网络请求
出现一个问题,接口正常返回时,可以进行倒计时操作,网络异常时,倒计时不执行
代码如下
网络请求方法:
private void requestHealth() {
mOpenAppUrl = Constant.mReleaseOpenAppUrl;
new LophealthAsyncTask(context, mOpenAppUrl, lopHealthCallBack).execute();
}
请求回调:
private LopCallBack lopHealthCallBack = new LopCallBack() {
@Override
public void lopSuccess(String strSuccess) {
try {
JSONObject jsonObject = new JSONObject(strSuccess);
if (jsonObject.has("code") && 200 == jsonObject.getInt("code") && jsonObject.has("data") && 0 != jsonObject.getInt("data")) {
int timeData = jsonObject.getInt("data");
countDownHealth(timeData);
}else {
countDownHealth(defautRequestTimes);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void lopError() {
countDownHealth(defautRequestTimes);
}
};
倒计时:
private void countDownHealth(int time) {
LogUtils.e(“lopSuccess”, "time : " + time);
CountDownTimer timer = new CountDownTimer(time, 1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
requestHealth();
cancel();
}
}.start();
}
异步请求:
public class LophealthAsyncTask extends AsyncTask<String, Integer, String> {
@SuppressLint("StaticFieldLeak")
private Context mContext;
private LopCallBack lopCallBack;
private String mUrl = "";
/**
* @param mContext
* @param url
* @param productIds
* @param channelCode
* @param gaid
*/
public LophealthAsyncTask(Context mContext, String url, LopCallBack lopCallBack) {
this.mContext = mContext;
this.mUrl = url;
this.lopCallBack = lopCallBack;
}
/**
* @param params
* @return str
*/
@Override
protected String doInBackground(String... params) {
String str = "";
InputStream inputStream = null;
try {
URL url = new URL(mUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept-Encoding", "identity");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
setConnHeader(conn);
conn.setDoInput(true);
if (HttpURLConnection.HTTP_OK == conn.getResponseCode()) {
LogUtils.e("success", " - - - - - " + conn.getResponseCode());
inputStream = conn.getInputStream();
str = streamToString(inputStream);
LogUtils.e("success----", " - - - - - " + conn.getResponseCode());
} else {
LogUtils.e("error", conn.getResponseMessage() + " - - - - - " + conn.getResponseCode());
lopCallBack.lopError();
}
conn.disconnect();
} catch (Exception e) {
LogUtils.e("error----", e.getMessage());
lopCallBack.lopError();
}
return str;
}
/**
* set header parameter
*
* @param conn
*/
private void setConnHeader(HttpURLConnection conn) {
try {
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept-Encoding", "identity");
} catch (Exception e) {
LogUtils.e("header", e.getMessage());
}
}
@Override
protected void onPostExecute(String result) {
if (!TextUtils.isEmpty(result)) {
LogUtils.e("success", result);
lopCallBack.lopSuccess(result);
}
}
/**
* @param is
* @return Convert input stream information to strings
*/
private String streamToString(InputStream is) {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String con;
try {
while ((con = reader.readLine()) != null) {
builder.append(con);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return builder.toString();
}
private String reverseString(String str) {
char[] ch = str.toCharArray();
StringBuffer sb = new StringBuffer();
for (int i = ch.length - 1; i >= 0; i--) {
sb.append(ch[i]);
}
return sb.toString();
}
}
回调:
public interface LopCallBack {
void lopSuccess(String strSuccess);
void lopError();
}
一开始写的时候貌似没什么问题,信心满满的就上线了
上线后才发现很多问题,最明显的几个
1,CountDownTimer,每执行一次就new 一个新线程,这得出现多少个?,而且cancel();位置也不对。
2,频繁请求未做处理
3,AsyncTask异常处理存在严重逻辑错误
而目前存在的测试错误是,当响应失败时,不会自动请求,直接停止了!
首先考虑以上问题,一点一点分析
问题1: 加全局控制
CountDownTimer timer;
private void countDownHealth(int time) {
if (timer != null) {
timer.cancel();
timer = null;
}
timer = new CountDownTimer(time, 1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
requestHealth();
}
}.start();
}
问题2:加一个简单的时间判断
private long defautRequestInterval = 60 * 1000;
private void requestHealth() {
LogUtils.e("requestHealth", "requestHealth");
long currentTime = System.currentTimeMillis();
if (currentTime - formerTime > defautRequestInterval) {
formerTime = System.currentTimeMillis();
mOpenAppUrl = Constant.mReleaseOpenAppUrl;
LogUtils.e("request", "health");
new LophealthAsyncTask(context, mOpenAppUrl,lopHealthCallBack).execute();
}
}
问题3: 首先考虑doInBackground、onPostExecute 在请求中的作用
doInBackground设置内容,onPostExecute是请求的回调
那lopCallBack.lopError() 放在 doInBackground就是错误的
应该在onPostExecute中执行
所以需要修改的是:
@Override
protected void onPostExecute(String result) {
if (!TextUtils.isEmpty(result)) {
LogUtils.e("success", result);
if (lopCallBack != null) {
lopCallBack.lopSuccess(result);
}
} else {
if (lopCallBack != null) {
lopCallBack.lopError();
}
}
}
调试总结:
**1,**写log,最好是每一行都加log,下一行的log打印了,上面的内容基本就执行了
**2,**写代码之前,一定要弄懂自己写的东西的原理,
最近发现很多写过的东西,都是反复修改,原因就是不懂原理,就知道复制粘贴,
用了之后不懂如何修改优化,出问题时,因为不懂原理找不到问题所在,容易考虑错误的方向。
出问题时千万不能,一直以为是别人的错误,认为以前是好好的,为什么会出错,然后就返回验证,去看,不去尝试。
如何尝试,首先要加log,定位问题的大致位置,查看添加的代码原理,先懂这个再去改。总是不相信代码有错,肯定浪费时间。
这也是提高效率的方式,先定位,然后懂原理,动手尝试,才能找准错误的位置。
**3,**今天又从同事那里学到一个技术点:
还是上面这个问题,我在加log测异常时,一直都只能执行到这句
LogUtils.e("lopSuccess", "time : timer" );
然后同事看到后,先在new CountDownTimer 后面加一个 log,证明这个会执行
然后看前后调用的逻辑,当看到执行的这个lopHealthCallBack时,打开了AsyncTask ,立马考虑到异步操作的逻辑问题,
然后就考虑异步会不会对我加的这个CountDownTimer 受到影响,知晓在doInBackground内执行的操作都是在线程内部的异步操作
然后就让我在把CountDownTimer的逻辑直接 放在了doInBackground中,发现,确实不执行,
这时,我正好想起 onPostExecute 里有callback 就成功了,
现在就很明显了,问题出在这里,应该在onPostExecute里才能调用CountDownTimer 成功
仔细一想AsyncTask的原理,onPostExecute就是对请求的响应,失败了也必须走这里
那这个问题就迎刃而解了