需求背景
项目没有使用成熟的第三方网络库(如Volley、Retrofit),而是使用为AsyncTask封装每一个网络请求API。
现在需要统一处理“未登录”的错误,即任意网络请求返回“未登录”错误时,能够被业务层代码捕获并处理。
但是之前的封装没有对应的钩子函数能够将错误交由业务逻辑层做统一的处理,或者写一个BaseAsyncTask来统一处理。
解决方案
注意到:
- 项目中所有的Activity都继承自抽象父类BaseActivity
- 所有AsyncTask都持有Activity的引用【存在内存泄漏的问题】
因此:
- 在BaseActivity中定义钩子方法
public abstract class BaseActivity extends AppCompatActivity {
...
/* 处理网络异常,在JSONAsyncTask中调用 */
public void onNetworkException() {
// Do something in subclass.
}
/**
* 处理网络错误代码,在JSONAsyncTask中调用
* @param status 网络请求返回的状态码
* @param msg 网络请求返回的信息
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onReqErrorCode(int status, String msg) {
if (status == 4006) {
// 处理未登录错误
Intent intent = new Intent(getApplicationContext(), WelcomeActivity.class);
startActivity(intent);
finish();
return true;
}
return false;
}
}
- 利用反射机制,在网络请求中调用上述两个钩子方法
public abstract class JSONAsyncTask extends AsyncTask<Object, Object, String> {
...
@Override
protected void onPostExecute(String resultStr) {
super.onPostExecute(resultStr);
stopProgressUI();
if (resultStr == null) {
// 将网络异常传递给BaseActivity,其子类通过重写onNetworkException来处理网络异常事件
try {
Method method = activity.getClass().getMethod("onNetworkException");
method.invoke(activity);
} catch (NoSuchMethodException e) {
Log.i(TAG, "activity has no onNetworkException() method");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
MyUtil.makeShortToast(this.activity, "#网络异常#");
return;
}
if (resultStr.isEmpty()) {
MyUtil.makeShortToast(this.activity, "#返回结果为空#");
return;
}
try {
JSONObject resultJSON = new JSONObject((resultStr));
int status;
status = resultJSON.has("status") ? resultJSON.getInt("status") : -1;
String msg = resultJSON.has("msg") ? resultJSON.getString("msg") : "";
if (status == 200) {
parseResult(resultJSON, msg);
} else {
boolean isSolved = false;
// 将网络请求的错误代码传递给BaseActivity,来统一处理网络请求的错误事件,如:未登录错误
try {
Method method = activity.getClass().getMethod("onReqErrorCode", int.class, String.class);
isSolved = (boolean) method.invoke(activity, status, msg);
} catch (NoSuchMethodException e) {
Log.i(TAG, "activity has no onReqErrorCode() method");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if (!isSolved) {
parseException(status, msg);
}
}
} catch (JSONException e) {
e.printStackTrace();
MyUtil.makeShortToast(this.activity, "#JSON数据解析失败#");
Log.e(TAG, "JSONStr:" + resultStr);
}
}
/**
* 重载此方法,设置url并添加Post的参数
*/
protected abstract void parseParams(Object... params);
/**
* 重载此方法,解析返回结果的data字段。此外还可以进行UI操作。
*
* @param resultJSON JSON数据对象
* @param msg 成功时返回的消息字段,由服务器端决定
* @throws JSONException JSON数据格式有误
*/
protected abstract void parseResult(JSONObject resultJSON, String msg) throws JSONException;
/**
* 用于处理不同的错误代码。子类可重新实现覆盖默认行为。
*
* @param status 状态吗
* @param msg 错误信息
*/
protected void parseException(int status, String msg) {
MyUtil.makeLongToast(this.activity, "请求失败\nstatus:" + status + "\nmsg:" + msg);
}
反思总结
- 处理网络请求的库是自己封装的,最初的考虑仅仅是将参数的设置、返回json的处理、载入UI的处理、请求的结果的回调进行了封装【这部分的内容代码之后跟进】。面对不断变化的需求,肯定存在很多的问题。就算是面对现在的需求变化,也是有些疲于应付。就如这次的修改,也是补丁式的修改。
- Context内存泄漏总结
- 通过查阅资料了解到,在默认情况下,多个AsyncTask是在同一个线程下串行执行的。这样保证了多个Task的顺序执行。(初代版本的安卓是这样设计的,之后的某几个版本修改为默认5个线程并行执行,但之后又修改为串行执行,但是添加了executeOnExecutor方法允许开发者并行执行多个Task)关于多种线程池的分析与总结
public abstract class AsyncTask<Params, Progress, Result> {
...
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
/* 传入了默认的串行Executor */
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
/* 限制了AsyncTask只允许执行一次 */
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture); // 执行异步操作
return this;
}
...
}