title: Android animation only starts after AsyncTask completes
body: <p>I am trying to create a native login screen, where the username and password boxes slide off the screen while the request is being processed (and slide back up if the login is unsuccessful). </p>
  
  <p>In order to achieve that, I have defined my animation (<code>DropDownAnimation</code>) and I assign it to my <code>LinearLayout</code> (footer). When the user clicks the Login button, I start the animation, and then call a function (<code>tryLogin()</code>) which starts an <code>AsyncTask</code>. The <code>AsyncTask</code> handles all the work of creating and sending the login request, and getting the <code>JSONObject</code> response.</p>
  
  <p>However, my problem is that the <code>slideDown</code> animation doesn't start until after the <code>AsyncTask</code> has completed. This doesn't look so bad on a successful login, but on a failed login it means that the <code>LinearLayout</code> never slides down - it jumps to the bottom of the screen, to begin the <code>slideUp</code> animation back to its original position.</p>
  
  <p>This seems like a similar problem to <a href="https://stackoverflow.com/questions/2703833/progressdialog-does-not-display-until-after-asynctask-completes">this question</a>, but I'm not doing using <code>bindService()</code> and all my non-UI code seems (to me) to be contained in the <code>AsyncTask</code> already. LogCat tells me:</p>
  
  <p><code>06-24 04:37:35.141: I/Choreographer(5347): Skipped 137 frames!  The application may be doing too much work on its main thread.</code></p>
  
  <p>I assume those are the frames where the footer would be sliding down - but I can't figure out where it is that I'm executing things on the main thread. Here's my code for LoginPage and LoginTask.</p>
  
  <p><strong>LoginPage.java</strong></p>
  
  <pre><code>@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);
          }
      }
  }
  </code></pre>
  
  <p><strong>LoginTask.java</strong></p>
  
  <pre><code>class LoginTask extends AsyncTask&lt;String, Void, JSONObject&gt; {
      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&amp;username="+params[0]+"&amp;password="+params[1]+"&amp;client_id="+params[3]+"&amp;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;
      }
  }
  </code></pre>
  
  <p>I've also tried making <code>LoginTask</code> be a member class of <code>LoginPage</code>, and starting the animation in the <code>onPreExecute()</code> method, but that didn't change anything.</p>
  
  <p>Any help is much appreciated!</p>