2012-01-07 27 views
1

我決定學習Scala/Play(服務器端),並決定學習Android(客戶端)遊戲開發,同時爲之加油開發。
我有一個關於如何在Android中爲HTTP請求做一個很好的設計的問題。
從我所瞭解的最佳方法是將HTTP請求委託給擴展抽象AsyncTask類的類。
您是否需要爲doInBackground方法中的每個不同邏輯在AsyncTask上重新進行擴展?
對我來說,每個請求邏輯都有一個類並不是很自然,而是在一個類中封裝了幾個相關的方法。如何建模Android AsyncTask類?

我剛開始玩一點,但是我對設計不滿意,因爲我不喜歡我在doInBackground(Object... params)中設計的可變參數對象。
使用這種設計,我放鬆了類型安全性,params對象遠非直觀和直觀,這是我在代碼中努力追求的。

這是我想要改進的代碼。

public class GameActivity extends Activity { 

    private class MyCellListener implements ICellListener { 
     public void onCellSelected() { 
      ServerProxy.postSelectedCell(row, col, player.getUser());  
      ... 
      // ServerProxy.other(); 

public class ServerProxy extends AsyncTask<Object, Void, Void>{ 

    private static final String TAG = ServerProxy.class.getSimpleName(); 
    private static final String SERVER_ADDRESS = "http://127.0.0.1"; 

    // Prevent external instantiation 
    private ServerProxy(){}; 

    public static void postSelectedCell(int row, int cell, User user){ 
     List<NameValuePair> postParameters = new ArrayList<NameValuePair>(3); 
     postParameters.add(new BasicNameValuePair("row", String.valueOf(row))); 
     postParameters.add(new BasicNameValuePair("cell", String.valueOf(cell))); 
     postParameters.add(new BasicNameValuePair("userName", user.getUserName())); 
     new ServerProxy().doInBackground("setSelectedCell" , postParameters); 
    } 

// public static void postOther() { 
//  new ServerProxy().doInBackground("other" , //some parameters); 
// } 

    /** 
    * @param postParameters First object URL postfix<br/> 
    * Second parameter is post parameters inform of {@code List<NameValuePair>} 
    * @return null 
    */ 
    @SuppressWarnings("unchecked") 
    @Override 
    protected Void doInBackground(Object... params) { 

     HttpClient httpclient = new DefaultHttpClient(); 
     HttpPost httppost = new HttpPost(SERVER_ADDRESS +"/" + params[0]); 
     httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); 

     try { 
      httppost.setEntity(new UrlEncodedFormEntity((List<NameValuePair>) params[1])); 
      httpclient.execute(httppost); 
     } catch (ClientProtocolException e) { 
     Log.e(TAG,e.toString()); 
     } catch (IOException e) { 
     Log.e(TAG,e.toString()); 
     } 
     return null; 
    } 
} 

回答

5

我想你不明白的AsyncTask做什麼。您不能在單個AsyncTask子類上定義像postLastName,postFirstName,postWhatever這樣的方法,並讓它執行該方法調用UI線程。 AsyncTask旨在使後臺作業更容易,無需直接處理線程,就可以更新UI。

爲了與您使用HttpClient的服務器進行通信,並且調用HttpClient.execute()將會阻塞,直到服務器的響應返回。這可能需要很長時間,尤其是在服務器繁忙,死亡或單元無線電關閉的情況下。當該通話時間過長時,您不希望用戶界面停止響應用戶。你想展示一個微調旋轉讓用戶知道正在發生的事情。如果您使用了您提供的UI的代碼,則必須直到繪製爲止,直到HttpClient.execute()調用返回爲止,因爲您在UI線程上調用它。

解決此問題的方法是將此調用關閉到另一個線程的UI線程。讓該線程等待響應,然後讓它通知它已完成的UI線程,並使用UI線程用新數據更新UI。爲什麼你不能讓後臺線程更新UI?因爲這會違反Android的線程規則,只有UI線程才能更新UI。

AsyncTask允許您在UI線程(doInBackground())上運行某些東西,並將返回值發佈到UI線程(onPostExecute()),以便它可以安全地更新UI而不違反Android的規則。您不直接調用doInBackground()或onPostExecute(),而是調用AsyncTask.execute(),AsyncTask中的代碼將在後臺線程上調用doInBackground(),並在完成時調用onPostExecute() 。這樣你就不會因爲自己完成所有的Threading而感到困惑。

現在,您的想法是讓多個請求通過一個子類運行,因爲AsyncTask將兩部分服務調用聯繫在一起:執行服務調用以獲取響應的詳細信息,以及如何處理該響應更新UI。如果調用postFirstName()方法,則在返回該響應後,如果調用postLastName(),則可能會有所不同。而且因爲它不同意味着你不能爲所有這些不同的調用定義一個AsyncTask。您可以通過組合或子類化基類來共享代碼,但是您必須爲要在服務器上執行的每個獨特操作創建一個子類。因此,每個電話都要考慮Class而不是Method。

您不必使用傳遞給doInBackground()的參數。如果你有多種類型的參數傳遞給AsyncTask。將它們傳遞給構造函數。因爲你不能重用AsyncTask實例(即每個實例不能多次調用AsyncTask.execute())。他們的生命週期必須實例化,執行()和折騰。這意味着在構造函數中傳遞輸入不會影響您使用AsyncTask的能力。我寫了自己的AsyncTask版本,分離出三種回調方法:success(),用於處理從doInBackground()返回的結果; handleException()在doInBackground()拋出異常時調用;和doFinally()無論doInBackground()如何調用都會返回一個或者拋出異常。在默認情況下,無論錯誤還是成功,AsyncTask都會調用onPostExecute()。這將是這個樣子:

public class MyTask extends EnhancedAsyncTask<Param,Integer,MyResult> { 
    MyParam1 param1; 
    MyParam2 param2; 

    public MyTask(MyParam1 param1, MyParam2 param2) { 
     this.param1 = param; 
     this.param2 = param2; 
    } 

    protected MyResult doInBackground(Param... params) { 
     // do server work here 
     server.send(param1, param2); 
    } 

    protected void success(MyResult result) { 
     // do Update of the UI 
    } 

    protected void handleException(Exception ex) { 
     // show an error here 
    } 
} 

// Now to use this AsyncTask you would do something like this: 

MyTask task = new MyTask(param1, param2).execute(); 

經常,我這樣做,因爲這樣引用傳遞到用戶界面的匿名內部類是不需要的,但如果你又沒有做匿名類你可以只通過那些給構造函數。如果Activity被破壞(比如用戶翻轉屏幕),你只需要小心不要觸摸UI。

+0

我很感謝你的回答,但我認爲你錯誤地解釋了我的代碼/問題,或者我對自己的描述不好。你寫我如果我做兩次往返,我需要兩個實例。我會這樣做,因爲我所有的公共方法調用都使用新的ServerProxy()實例化一個新實例。如果我錯了,請糾正我,因爲你的代碼看起來不錯,但它根本不能解決我的問題?問題是在一個類中有許多不同類型的請求。例如一個Server類,以某種方式擴展AsyncTask,包含許多方法postFirstName,postLastName,getAllFriends等全部具有唯一參數。 – Farmor 2012-01-07 23:32:13

+0

我更新了我的答案,以更好地解釋AyncTask實際上做了什麼,因爲我不認爲你瞭解它的目的。 – chubbsondubs 2012-01-08 03:36:07

0

在設計之前,有些事情看起來像你做錯了。

  • 你不應該直接調用doInBackground,你在這裏做

    新ServerProxy()doInBackground( 「setSelectedCell」,postParameters)。

    而是應該調用任何version of execute

至於設計,你可以試試這個

  • 您可以創建一個ServerProxy類和不從AsyncTask擴展它。只需使用它來實現邏輯封裝。
  • 現在你可以創建一個ServerProxyAsync這將採取ServerProxy作爲組成。
  • 現在一個選項,你可以做的是

    公共類ServerProxy擴展的AsyncTask

    這裏String可能是你想在ServerProxy實例調用的方法名稱,你可以使用反射了點。

注意:有可能是其他SOLN這個概率,這只是第一次嘗試的問題