Android開源框架Afinal第一篇——揭開聖女的面紗


Afinal

這是Afinal在github的地址:https://github.com/yangfuhai/afinal

Afinal這個框架主要分4塊:

1、FinalDB模塊:android中的orm框架,一行代碼就可以進行增刪改查。支持一對多,多對一等查詢。

2、FinalActivity模塊:android中的ioc框架,完全注解方式就可以進行UI綁定和事件綁定。無需findViewById和setClickListener等。

3、FinalHttp模塊:通過httpclient進行封裝http數據請求,支持ajax方式加載。

4、FinalBitmap模塊:通過FinalBitmap,imageview加載bitmap的時候無需考慮bitmap加載過程中出現的oom和android容器快速滑動時候出現的圖片錯位等現象。FinalBitmap可以配置線程加載線程數量,緩存大小,緩存路徑,加載顯示動畫等。FinalBitmap的內存管理使用lru算法,沒有使用弱引用(android2.3以后google已經不建議使用弱引用,android2.3后強行回收軟引用和弱引用,詳情查看android官方文檔),更好的管理bitmap內存。FinalBitmap可以自定義下載器,用來擴展其他協議顯示網絡圖片,比如ftp等。同時可以自定義bitmap顯示器,在imageview顯示圖片的時候播放動畫等(默認是漸變動畫顯示)。

 

這里我們先講FinalHttp模塊,這是它的用法:

AjaxParams params = new AjaxParams();
  params.put("username", "michael yang");
  params.put("password", "123456");
  params.put("email", "test@tsz.net");
  params.put("profile_picture", new File("/mnt/sdcard/pic.jpg")); // 上傳文件
  params.put("profile_picture2", inputStream); // 上傳數據流
  params.put("profile_picture3", new ByteArrayInputStream(bytes)); // 提交字節流

  FinalHttp fh = new FinalHttp();
  fh.post("http://www.yangfuhai.com", params, new AjaxCallBack(){
        @Override
        public void onLoading(long count, long current) {
                textView.setText(current+"/"+count);
        }

        @Override
        public void onSuccess(String t) {
            textView.setText(t==null?"null":t);
        }
  });

大家看到了吧,fh.get(baseUrl, params, new AjaxCallBack(){});

這句話的底層就是HttpClient執行HttpGet或者HttpPost請求的一個封裝,其中baseUrl+params拼湊起來就是url,而后面的AjaxCallBack就是對HttpClient對http請求得到的一個HttpResponse響應結果的分析回調。

我們來看具體的流程步驟:

第一步:初始化FinalHttp,然后執行get()方法

public void get( String url, AjaxParams params, AjaxCallBack<? extends Object> callBack) {
        sendRequest(httpClient, httpContext, new HttpGet(getUrlWithQueryString(url, params)), null, callBack);
    }

第二步:我們看sendRequest()這個方法,這個方法就是把get方法里的參數傳遞到這個函數,然后發起http請求。

protected <T> void sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, AjaxCallBack<T> ajaxCallBack) {
        if(contentType != null) {
            uriRequest.addHeader("Content-Type", contentType);
        }
        new HttpHandler<T>(client, httpContext, ajaxCallBack,charset)
        .executeOnExecutor(executor, uriRequest);

    }

這是一個保護級的方法,其實還是一個馬甲,正真執行的還是那個HttpHandler里的exe方法。我們看到這個方法的參數有DefaultHttpClient,這個都不用說了大家都很熟悉,HttpContext就是一個上下文,HttpUriRequest就是一個get,put,delete,post等http請求,然后就是添加頭部信息,比如要不要進行壓縮,這里貌似用了zip壓縮使得傳輸速率更快,再后面就是回調函數接口,判斷請求是否成功失敗等。

第三步:進入HttpHandler類,這是一個處理http請求的類。這個類是繼承AsyncTask,但是這個AsyncTask類並不是原生的,而是經過作者精心修改的。沒有深入去看AsyncTask,大略看了一下,貌似doInBackground這個方法是放進一個線程池里去執行的。這個線程池配置的很精細,就像批量加載圖片那個例子,可以只想你個完上一個線程,就馬上執行下一個任務。

1.這個類還實現了EntityCallBack回調接口,並且接收了AjaxCallBack這個回調實例。我們先看doInBackground方法。

protected Object doInBackground(Object... params) {
        if(params!=null && params.length == 3){
            targetUrl = String.valueOf(params[1]);
            isResume = (Boolean) params[2];
        }
        try {
            publishProgress(UPDATE_START); // 開始
            makeRequestWithRetries((HttpUriRequest)params[0]);
            
        } catch (IOException e) {
            publishProgress(UPDATE_FAILURE,e,e.getMessage()); // 結束
        }

        return null;
    }

2.可以看到真正執行的方法是makeRequestWithRetries(HttpUriRequest *)方法,進去看看先

private void makeRequestWithRetries(HttpUriRequest request) throws IOException {
        if(isResume && targetUrl!= null){
            File downloadFile = new File(targetUrl);
            long fileLen = 0;
            if(downloadFile.isFile() && downloadFile.exists()){
                fileLen = downloadFile.length();
            }
            if(fileLen > 0)
                request.setHeader("RANGE", "bytes="+fileLen+"-");
        }
        
        boolean retry = true;
        IOException cause = null;
        HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
        while (retry) {
            try {
                if (!isCancelled()) {
                    HttpResponse response = client.execute(request, context);
                    if (!isCancelled()) {
                        handleResponse(response);
                    } 
                }
                return;
            } catch (UnknownHostException e) {
                publishProgress(UPDATE_FAILURE, e,"unknownHostException:can't resolve host");
                return;
            } catch (IOException e) {
                cause = e;
                retry = retryHandler.retryRequest(cause, ++executionCount,context);
            } catch (NullPointerException e) {
                // HttpClient 4.0.x 之前的一個bug
                // http://code.google.com/p/android/issues/detail?id=5255
                cause = new IOException("NPE in HttpClient" + e.getMessage());
                retry = retryHandler.retryRequest(cause, ++executionCount,context);
            }catch (Exception e) {
                cause = new IOException("Exception" + e.getMessage());
                retry = retryHandler.retryRequest(cause, ++executionCount,context);
            }
        }
        if(cause!=null)
            throw cause;
        else
            throw new IOException("未知網絡錯誤");
    }

其中isResume是斷點續傳標志,targetUrl判斷是文件的話就添加文件大小頭部信息。retry是遇到錯誤是否要重試標志,FinalHttp有這個參數配置,可以設置重試次數。然后就是HttpClient執行Http請求獲取響應的操作,如果這個過程中UI上的交互有取消操作的話。可以通過isCancelled()這個方法得知從而取消請求。如果請求順利會得到一個HttpResponse,而處理這個響應結果是handleResponse()方法。

3.這是專門處理響應結果的方法

private void handleResponse(HttpResponse response) {
        StatusLine status = response.getStatusLine();
        if (status.getStatusCode() >= 300) {
            String errorMsg = "response status error code:"+status.getStatusCode();
            if(status.getStatusCode() == 416 && isResume){
                errorMsg += " \n maybe you have download complete.";
            }
            publishProgress(UPDATE_FAILURE,new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()),errorMsg);
        } else {
            try {
                HttpEntity entity = response.getEntity();
                Object responseBody = null;
                if (entity != null) {
                    time = SystemClock.uptimeMillis();
                    if(targetUrl!=null){
                        responseBody = mFileEntityHandler.handleEntity(entity,this,targetUrl,isResume);
                    }
                    else{
                        responseBody = mStrEntityHandler.handleEntity(entity,this,charset);
                    }
                        
                }
                publishProgress(UPDATE_SUCCESS,responseBody);
                
            } catch (IOException e) {
                publishProgress(UPDATE_FAILURE,e,e.getMessage());
            }
            
        }
    }

通過狀態碼,判斷成功后處理得到的HttpEntity。如果這個response是字符串就由StringEntityHandler這個類去解析,如果是文件就是FileEntityHandler解析。得到結果后,就是publishProgress去推送了,這個方法在前面的方法中都看到有被調用。稍微對AsyncTask熟悉的人就知道這個方法將可以觸發onProgressUpdate(* value).這個方法就是告訴你執行的進度,AfinalHttp中AjaxCallback中的onSuccess,onFailure等方法就是根據這個來觸發的。

   private final static int UPDATE_START = 1;
    private final static int UPDATE_LOADING = 2;
    private final static int UPDATE_FAILURE = 3;
    private final static int UPDATE_SUCCESS = 4;

到這里這個過程就完成了。

看到這么多的處理,會覺得這樣是不是會更慢。一個網絡請求處理的極其復雜,想FinalHttp很多默認的配置和繁雜的處理響應並通知處理進程,其實這樣多多少少拖慢了請求的速度。在實際應用尤其感受深切,一遇到網絡慢,跨地域幅度大等教為惡劣的網絡環境,AFinal就有點顯得捉襟見肘了。

由於篇幅限制,這次就講這么多了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM