做过Android手机开发的人都知道,手机UI是一个单独的线程在运行,并且该线程最好不会因为用户的操作而阻塞。换句话说,如果用户进行的操作需要耗时几十秒甚至几十分钟,那么在这段时间内占用UI线程是一个非常不明智的做法。它会阻塞掉UI线程,导致手机不再显示或者接受用户新的操作,给用户一种死机的感觉。
因此最好的方法是将用户耗时较长的操作放到另一个线程中去,并且用监听者模式来监听操作的完成。比如,手机服务通过http协议与服务器建立通讯,为了使此通讯不会阻塞手机其他功能的显示,需要开辟一个新线程传输数据,一旦数据传输完毕,则会调用监听者的回调函数,将结果显示在手机屏幕上。本文将介绍一种UI和耗时操作分离的方法:
每个复杂操作我们需要将其抽象成一个Task:
//定义一个BaseTask基类,其中taskParameter是需要执行任务所要传递的参数
//而execute()函数将在派生类中被重写
public
abstract
class
BaseTask { Object taskParameter;
abstract
Object execute() throws Exception;
}在基类基础上,定义派生类:
public
class
AsynMethodTask extends BaseTask {
public
AsynMethodTask(AsynMethodTaskParameter taskParameter) {
this
.taskParameter = taskParameter;
}
//重写execute函数,这里利用了Java提供的Method类,instance是需要执行的类,而methodName是需要执行的方法,methodParameter是需要传递的参数
@Override
public
Object execute() throws Exception {
AsynMethodTaskParameter parameters = (AsynMethodTaskParameter)
this
.taskParameter;
Method method = MethodHelper.getMethod(parameters.instance,
parameters.methodName, parameters.methodParameters);
if
(method !=
null
) {
return
method.invoke(parameters.instance,
parameters.methodParameters);
}
return
null
;
}
//工厂模式,返回一个Task的实例
public
static
AsynMethodTask CreateTask(Object instance, String methodName,
Object... parameters) {
return
new
AsynMethodTask(
new
AsynMethodTaskParameter(instance,
methodName, parameters));
}
}
|
AsynMethodTaskParameter定义如下:
public
class
AsynMethodTaskParameter{
public
Object instance;
public
String methodName;
public
Object[] methodParameters;
public
AsynMethodTaskParameter (Object instance , String methodName,Object[] methodParameters )
{
this
.instance = instance;
this
.methodName = methodName;
this
.methodParameters = methodParameters;
}
}
|
这样,一个基本的TASK方法已经实现,那么如何使用它呢?我们需要一个Request类来发送请求,并且异步启用task:
public
class
Request extends AsyncTask<RequestParameter, Integer, Object> {
private
RequestListener emmaRequestListener =
null
;
private
Object data =
null
;
private
Exception _errorException =
null
;
//在后台异步启动TASK,AsyncTask提供了异步功能,需要重载其中的回调函数
@Override
protected
Object doInBackground(RequestParameter... parameter) {
try
{
RequestParameter emmaRequestParameter = parameter[0];
emmaRequestListener = emmaRequestParameter.requestListener;
data = emmaRequestParameter.data;
BaseTask task = emmaRequestParameter.task;
return
task.execute();
}
catch
(InvocationTargetException e) {
Throwable baseException = e.getCause() ;
if
(baseException ==
null
)
_errorException =
new
LocalGeneralException(e.getMessage());
else
{
if
(baseException instanceof ServerGeneralException)
{
_errorException =
new
ServerGeneralException(baseException.getMessage());
}
else
if
(baseException instanceof ServerAuthException)
{
_errorException =
new
ServerAuthException(baseException.getMessage());
}
else
{
_errorException =
new
LocalGeneralException(baseException.getMessage());
}
}
}
catch
(Exception e) {
_errorException =
new
LocalGeneralException(e.getMessage());
}
return
_errorException;
}
//执行结束后,如果没有异常,则调用回调函数
@Override
protected
void
onPostExecute(Object result) {
if
(_errorException ==
null
) {
emmaRequestListener.onRequestSuccess(result, data);
}
else
{
emmaRequestListener.onRequestFailed(_errorException, data);
}
}
//中途如果被cancel掉
@Override
protected
void
onCancelled() {
emmaRequestListener.onRequestCanceled(data);
}
public
interface
RequestListener {
void
onRequestSuccess(Object result, Object data);
void
onRequestFailed(Object result, Object data);
// void onRequestCanceled(Object data);
}
//定义执行过程,需要加入task,如果成功会调用RequestListener所定义的函数
public
static
Request executeAsynRequest(BaseTask task,
RequestListener requestListener, Object data) {
RequestParameter requestParameter =
new
RequestParameter(task,
requestListener, data);
Request r =
new
Request();
r.execute(requestParameter);
return
r;
}
}
|
最后我们只需要定义好Method的名字:
public
final
static
class
Methods
{
public
static
String getProfileView =
"getProfileView"
;
public
static
String uploadProfileView =
"uploadProfileView"
;
public
static
String getUserProfile =
"getUserProfile"
;
}
|
就可以正常调用了:
AsynMethodTask task = AsynMethodTask.CreateTask(profileModel, ProfileModel.Methods.uploadProfileView, profileUploadData);
Request.executeAsynRequest(task,
this
, TASK_UPLOAD_PROFILE);
|
整个过程相当简单,最重要的是熟悉观察者模式,以及AsyncTask类的运行方式,重载其中的回调函数,让我们的任务在后台运行。返回的值为Object对象,需要通过强制类型转换成为我们需要的值。而传递进去的参数可以通过无限制数目的参数传递。