2013-06-24 4 views
1

Я пытаюсь создать собственный экран входа в систему, где поля имени пользователя и пароля сдвигаются с экрана во время обработки запроса (и сползают назад, если вход не увенчается успехом).Android-анимация начинается только после завершения AsyncTask

Для этого я определил свою анимацию (DropDownAnimation), и я назначил ее моему LinearLayout (нижний колонтитул). Когда пользователь нажимает кнопку «Вход», я запускаю анимацию, а затем вызываю функцию (tryLogin()), которая запускает AsyncTask. AsyncTask обрабатывает всю работу по созданию и отправке запроса на вход и получает ответ JSONObject.

Однако, моя проблема заключается в том, что анимация slideDown начинается не после завершения AsyncTask. Это не выглядит так плохо при успешном входе в систему, но при неудачном входе в систему это означает, что LinearLayout никогда не скользит вниз - он перескакивает в нижнюю часть экрана, чтобы начать анимацию slideUp обратно в исходное положение.

Это кажется подобной проблемой для 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(), вы блокируете поток пользовательского интерфейса. Поскольку анимация работает в потоке пользовательского интерфейса, она выглядит так, как будто она не работает (хотя на самом деле она заблокирована вашим длительным методом tryLogin).

Вместо этого, вы должны переместить код, который зависит от результата от LoginTask его метода onPostExecute:

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

Благодаря Ян, это прекрасно работало. В вашем примере кода есть несколько ошибок (в основном контекстные вещи - у меня нет доступа к 'load' или' footer' в контексте 'LoginTask', а также' Toast.makeText' не принимает 'LoginTask' как аргумент). Из-за этого мне нужно было сделать 'LoginTask' класс-член' LoginPage' и ссылаться на все через 'LoginPage'. Есть ли способ избежать этого? Или мне просто нужно принять все эти функции в одном файле? –

+0

@MattRowland - нет причин, по которым вы не можете передавать 'Context' и' TextView 'в свой конструктор' LoginTask' (который в настоящее время является стандартным, без конструктора параметров). – ianhanniballake

+0

Ах, отлично! Я действительно не знал, что Context можно использовать так. Думаю, это действительно улучшило мой код. Еще раз спасибо, Ян! –