簡體   English   中英

如何為Android AsyncTask類建模?

[英]How to modell an Android AsyncTask class?

我決定學習Scala / Play(服務器端),並決定同時學習Android (客戶端)游戲開發,以使開發更加有趣。
我有一個關於如何在Android中為HTTP請求做一個不錯的設計的問題。
據我了解,最好的方法是將HTTP請求委托給擴展抽象AsyncTask類的類。
您是否需要為您重寫的doInBackground方法中的每個不同邏輯做AsyncTask的新擴展?
對我而言,為每個請求邏輯都擁有一個類而不是將幾個連貫的方法封裝在一個類中並不自然。

我剛剛開始玩一些游戲,但是我對設計不滿意,因為我不喜歡doInBackground(Object... params)中的varargs Object設計。
通過這種設計,我失去了類型安全性,並且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;
    }
}

我認為您不了解AsyncTask的功能。 您無法在單個AsyncTask子類上定義諸如postLastName,postFirstName,postWhatever之類的方法,並讓其執行該方法以調用UI線程。 AsyncTask的目的是使執行需要更新UI的后台作業變得更容易,而不會強迫您直接處理線程。

為了與服務器通信,您使用的是HttpClient,調用HttpClient.execute()將阻塞,直到服務器的響應返回為止。 這可能會花費很長時間,尤其是在服務器繁忙,死機或蜂窩無線電關閉的情況下。 當該調用花費的時間太長時,您不希望您的UI停止響應用戶。 您想顯示一個微調器,讓用戶知道正在發生的事情。 如果您使用的代碼是您提供的,則您的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(),完成后它將在UI線程上調用onPostExecute()。 。 這樣一來,您就不會為自己做所有的Thread所困擾。

現在,讓多個請求通過一個子類運行的想法將行不通,因為AsyncTask將進行服務調用的兩個部分聯系在一起:執行服務調用以獲取響應的細節以及如何處理該響應以進行更新用戶界面。 如果調用postFirstName()方法,則返回該響應后所執行的操作可能與調用postLastName()時不同。 並且因為它是不同的,所以您不能只為所有這些不同的調用定義一個AsyncTask。 您可以通過組合或子類化基類來共享代碼,但是您必須為要在服務器上執行的每個唯一操作創建一個子類。 因此,對於每個調用,請考慮使用Class而不是Method。

您不必使用傳遞給doInBackground()的參數。 如果您有多種類型的參數要傳遞給AsyncTask。 將它們傳遞給構造函數。 由於您不能重用AsyncTask實例(即,每個實例不能多次調用AsyncTask.execute())。 它們的生命周期必須實例化,execute()和折騰。 這意味着在構造函數中傳遞輸入不會損害您使用AsyncTask的能力。

我編寫了自己的AsyncTask版本,該版本分離出三種回調方法:success()用於處理從doInBackground()返回的結果; 從doInBackground()引發任何異常時調用handleException()。 和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();

通常,我會以匿名內部類的形式進行操作,因此不需要傳遞對UI的引用,但是同樣,如果您不執行Anon類,則可以將其傳遞給構造函數。 您只需要注意不要破壞活動(如用戶翻轉屏幕),不要觸摸用戶界面。

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

  • 您不應該像在這里doInBackground直接調用doInBackground

    新的ServerProxy()。doInBackground(“ setSelectedCell”,postParameters);

    相反,它應該調用任何版本的execute

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

  • 您可以創建ServerProxy類,並且不要從AsyncTask對其進行擴展。 只需使用它就可以封裝您的邏輯。
  • 現在,您可以創建一個ServerProxyAsync ,它將ServerProxy作為組成。
  • 現在您可以做的一個選擇是

    公共類ServerProxy擴展了AsyncTask

    在這里, String可以是您要在ServerProxy實例上調用的方法名稱,並且可以為此使用反射。

注意:此問題可能還有其他解決方法,這只是解決問題的第一次嘗試

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM