Android 中UI与耗时操作的分离

       做过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对象,需要通过强制类型转换成为我们需要的值。而传递进去的参数可以通过无限制数目的参数传递。  

 

  转自http://www.apkbus.com/

 

  

 

posted @ 2012-08-17 17:18  amyy  阅读(1196)  评论(0编辑  收藏  举报