2013-06-24 44 views
1

我正嘗試創建一個本機登錄屏幕,其中用戶名和密碼框在請求正在處理時滑出屏幕(並且如果登錄失敗則向後滑動)。Android動畫只在AsyncTask完成後啓動

爲了達到這個目的,我定義了我的動畫(DropDownAnimation),並將其分配給我的LinearLayout(頁腳)。當用戶點擊登錄按鈕時,我開始動畫,然後調用一個啓動AsyncTask的函數(tryLogin())。 AsyncTask負責處理創建和發送登錄請求以及獲取JSONObject響應的所有工作。

但是,我的問題是,slideDown動畫直到AsyncTask完成後纔開始。這在成功登錄時看起來並不那麼糟糕,但在登錄失敗時,這意味着LinearLayout永遠不會滑落 - 它跳到屏幕底部,開始動畫回到其原始位置。

這看起來像一個類似的問題this question,但我沒有使用bindService()和我所有的非UI代碼似乎(在我)被包含在AsyncTask已經。 logcat的告訴我:

06-24 04:37:35.141: I/Choreographer(5347): Skipped 137 frames! The application may be doing too much work on its main thread.

我認爲這些都是在頁腳將向下滑動幀 - 但我無法弄清楚它是我在主線程上執行的事情。這是我的LoginPage和LoginTask的代碼。

LoginPage.java

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_login_page); 

    login = (Button) findViewById(R.id.login); 
    username = (EditText) findViewById(R.id.username); 
    password = (EditText) findViewById(R.id.password); 
    footer = (LinearLayout) findViewById(R.id.footer); 

    // We must wait for the layout to be finalised before trying to find heights. 
    ViewTreeObserver vto = footer.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      initAnimations(); 
     } 
    }); 

    loading = (TextView) findViewById(R.id.loading); 

    login.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      String mUsername = username.getText().toString(); 
      String mPassword = password.getText().toString(); 

          // Neither of these two things happen until after LoginTask is done. 
      footer.startAnimation(slideDown); 
      loading.setVisibility(TextView.VISIBLE); 

      tryLogin(mUsername, mPassword); 
     } 
    }); 
} 

protected void tryLogin(String mUsername, String mPassword) { 
    Exception e; 
    String loginUrl = getString(R.string.login_url); 
    String clientId = getString(R.string.client_id); 
    String clientSecret = getString(R.string.client_secret); 
    LoginTask loginTask = (LoginTask) new LoginTask().execute(mUsername, mPassword, loginUrl, clientId, clientSecret); 
    if ((e = loginTask.getException()) != null) { 
     Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show(); 
     Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); 
    } else { 
     JSONObject response; 
     try { 
      response = loginTask.get(); 
      Log.d("login", response.toString()); 
      if (!response.has("access_token")) { 
       loading.setVisibility(TextView.INVISIBLE); 
       footer.startAnimation(slideUp); 
       Toast.makeText(this, "Login error", Toast.LENGTH_LONG).show(); 
      } else { 
       Intent i = new Intent(this, FullscreenWebView.class); 
       i.putExtra("accessToken", response.get("access_token").toString()); 
       startActivity(i); 
       overridePendingTransition(0, 0); 
      } 
     } catch (InterruptedException e1) { 
      e1.printStackTrace(); 
      Thread.currentThread().interrupt(); 
     } catch (ExecutionException e1) { 
      e1.printStackTrace(); 
     } catch (JSONException e1) { 
      e1.printStackTrace(); 
      throw new RuntimeException(e); 
     } 
    } 
} 

LoginTask.java

class LoginTask extends AsyncTask<String, Void, JSONObject> { 
    private Exception exception; 

    @Override 
    protected JSONObject doInBackground(String... params) { 
     HttpURLConnection connection; 
     OutputStreamWriter request = null; 

     URL url = null; 
     JSONObject response = null; 
     String parameters = "grant_type=password&username="+params[0]+"&password="+params[1]+"&client_id="+params[3]+"&client_secret="+params[4]; 

     try { 
      url = new URL(params[2]); 
      connection = (HttpURLConnection) url.openConnection(); 
      connection.setDoOutput(true); 
      connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); 
      connection.setRequestMethod("POST"); 

      request = new OutputStreamWriter(connection.getOutputStream()); 
      request.write(parameters); 
      request.flush(); 
      request.close(); 

      // username or password is probably wrong 
      Log.d("login", ""+connection.getResponseCode()); 
      if (connection.getResponseCode() != 200) { 
       return new JSONObject(); 
      } 
      String line = ""; 
      InputStreamReader isr = new InputStreamReader(connection.getInputStream()); 
      BufferedReader reader = new BufferedReader(isr); 
      StringBuilder sb = new StringBuilder(); 
      while ((line = reader.readLine()) != null) { 
       sb.append(line + "\n"); 
      } 

      Log.d("login", sb.toString()); 
      response = new JSONObject(sb.toString()); 

      isr.close(); 
      reader.close(); 
     } catch (Exception e) { 
      this.exception = e; 
     } 

     return response; 
    } 
} 

我也試圖使LoginTask是一個成員類的LoginPage,和啓動動畫在onPreExecute()方法,但這並沒有改變任何東西。

任何幫助非常感謝!

回答

5

當您使用AsyncTask.get()時,您將阻止UI線程。當動畫在UI線程上運行時,它看起來好像沒有運行(實際上它被長時間運行的tryLogin方法阻止)。

相反,你應該將依賴於結果從LoginTaskonPostExecute方法的代碼:

protected void tryLogin(String mUsername, String mPassword) { 
    String loginUrl = getString(R.string.login_url); 
    String clientId = getString(R.string.client_id); 
    String clientSecret = getString(R.string.client_secret); 
    new LoginTask().execute(mUsername, mPassword, 
     loginUrl, clientId, clientSecret); 
} 

LoginTask.java

class LoginTask extends AsyncTask<String, Void, JSONObject> { 
    private Exception exception; 

    @Override 
    protected JSONObject doInBackground(String... params) { 
     // Unchanged 
    } 

    public void onPostExecute(JSONObject response) { 
     if (exception != null) { 
      Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show(); 
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); 
     } else { 
      Log.d("login", response.toString()); 
      if (!response.has("access_token")) { 
       loading.setVisibility(TextView.INVISIBLE); 
       footer.startAnimation(slideUp); 
       Toast.makeText(this, "Login error", Toast.LENGTH_LONG).show(); 
      } else { 
       Intent i = new Intent(this, FullscreenWebView.class); 
       i.putExtra("accessToken", response.get("access_token").toString()); 
       startActivity(i); 
       overridePendingTransition(0, 0); 
      } 
     } 
    } 
} 
+0

感謝伊恩,這真是棒極了。你的示例代碼中有幾個錯誤(主要是上下文的事情 - 我不能在'LoginTask'的上下文中訪問'loading'或'footer','Toast.makeText'也沒有'LoginTask'作爲論據)。 因此,我需要使'LoginTask'成爲'LoginPage'的成員類,並通過'LoginPage'引用所有內容。有沒有辦法避免這種情況?或者我只需要接受在一個文件中具有所有這些功能? –

+0

@MattRowland - 你沒有理由不能將'Context'和'TextView'傳遞給你的'LoginTask'的構造函數(它現在只是默認的,沒有參數構造函數)。 – ianhanniballake

+0

啊太棒了!我不知道上下文可以這樣使用。我認爲這確實改善了我的代碼。再次感謝伊恩! –