AsyncTask и мониторы - PullRequest
       5

AsyncTask и мониторы

2 голосов
/ 04 декабря 2011

Вот моя проблема:

Мне нужно сделать несколько запросов на сервере. Эти запросы должны быть сделаны один за другим, чтобы избежать смешивания. Для этого я использую мониторы.

Вот что я до сих пор придумал:

public class TestActivity extends Activity
{
  private String key;
  private HashMap<String, String> values;

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    values = new HashMap<String, String>();

    ArrayList<String> list = new ArrayList<String>();
    list.add("foo");
    list.add("bar");
    list.add("baz");

    createValues(list);
  }

  private void createValues(final ArrayList<String> list)
  {
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run()
      {
        key = null;
        for (String element : list)
        {
          if (key != null) // Every time except the first time.
          {
            synchronized (key)
            {
              try
              {
                key.wait();
              }
              catch (InterruptedException e)
              {
              }
            }
          }
          key = element;
          DataProcessor dataProcessor = new DataProcessor();
          dataProcessor.execute("Processed " + element);
        }
      }
    });
  }

  private void putDataInValue(String element)
  {
    synchronized (key)
    {
      values.put(key, element);
      key.notify();
    }
  }

  private class DataProcessor extends AsyncTask<String, Void, String>
  {
    @Override
    protected String doInBackground(String... params)
    {
      // Fetching data on a server. This takes time.
      try
      {
        Thread.sleep(10000);
      }
      catch (InterruptedException e)
      {
      }
      return params[0] + " from the server";
    }

    @Override
    protected void onPostExecute(String result)
    {
      putDataInValue(result);
    }
  }
}

После этого мне хотелось бы, чтобы содержание values было:

[
 "foo" => "Processed foo from the server",
 "bar" => "Processed bar from the server",
 "baz" => "Processed baz from the server"
]

Мне нужно сохранить значения в list и знать, какое из них соответствует какому контенту (следовательно, монитору).

Моя проблема в том, что в LogCat постоянно появляется сообщение об ошибке:

Can't create handler inside thread that has not called Looper.prepare()

Я искал в Интернете, нашел людей, у которых была эта проблема, посмотрел на ответы, большинство из которых сказали, что мне нужно использовать обработчики. Обработчики тоже не работают. Я пытался заменить нить на

handler = new Handler(Looper.getMainLooper);
handler.post(new Runnable //...
//...

но он просто зависает.

Я готов признать, что мой подход неверен и начать все заново, если вы думаете, что я в тупике. Что бы вы сделали?

1 Ответ

0 голосов
/ 17 марта 2012

Возможно, вы уже нашли решение для этого, но в любом случае, вот один из способов обеспечить последовательный порядок ваших запросов, используя Executor:

public class SequentialTaskExampleActivity extends Activity {

    /**
     * will only execute one job at a time, in the order given to it.
     */
    private Executor executor = Executors.newSingleThreadExecutor();
    private Map<String, String> values = new HashMap<String, String>();
    private TextView textView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        textView = new TextView(this);
        textView.setTextSize(24);
        setContentView(textView);

        // Initialise jobs and add to queue 
        ArrayList<String> list = new ArrayList<String>();
        list.add("foo");
        list.add("bar");
        list.add("baz");

        for (String key : list) {
            executor.execute(new Job(key));
        }
    }

    public void addToResult(String key, String value) {
        values.put(key, value);

        // display result to UI
        textView.setText(String.format("%s %s => %s\n", textView.getText(), key, value));
    }

    private class Job implements Runnable {
        private String key;

        public Job(String key) {
            this.key = key;
        }

        @Override
        public void run() {
            // simulate work
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // retrieve result
            final String value = key + " from the server";

            // post result back to UI
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    addToResult(key, value);
                }
            });
        }
    }
}

Если вы нацелены на API 11+, вы можете указать конкретного исполнителя для использования с AsyncTask (фактически, я думаю, что по умолчанию новое поведение - последовательная обработка). В любом случае вы всегда должны создавать и выполнять AsyncTask в потоке пользовательского интерфейса; это единственный способ обеспечить правильное поведение AsyncTask.onPostExecute().

...