Android 中UI與耗時操作的分離

jopen 10年前發布 | 44K 次閱讀 Android Android開發 移動開發

           做過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對象,需要通過強制類型轉換成為我們需要的值。而傳遞進去的參數可以通過無限制數目的參數傳遞。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!