Android: создать фоновый поток, который периодически запускается и выполняет задачи пользовательского интерфейса?

Итак, я знаю, как выполнять фоновую задачу, я знаю, как выполнять периодическую задачу (используя дескриптор postdelayed и runnable), я также знаю, как выполнять задачу пользовательского интерфейса из фонового потока (через обработчик), но я не могу выполнить периодическая фоновая задача, которая выполняет некоторые действия в потоке пользовательского интерфейса.

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

private void DoTask() {
        Thread thread = new Thread() {
            public void run() {
                Looper.prepare();
                final Handler handler = new Handler();
                handler.post(netRunnable);
                Looper.loop();
            }
        };
        thread.start();
}

Runnable netRunnable = new Runnable() {
    @Override
    public void run() {
        handler.getLooper().prepare();
        final Handler handler1 = new Handler(Looper.getMainLooper());
        if ( do background work and check result){
            handler1.post(new Runnable() {
                @Override
                public void run() {
                    //Do UI Task
                }
            });
        }
        handler.getLooper().loop();
        handler.postDelayed(netRunnable, 60000);
    }
}

Я понимаю, что в моей реализации могут быть некоторые фундаментальные недостатки, но я не знаю, как правильно выполнить эту задачу. Прямо сейчас он выдает ошибку, что Only one Looper may be created per thread. Я понимаю, что он пытается сказать. Но может ли кто-нибудь предложить сделать это правильно.


person varunkr    schedule 09.10.2015    source источник
comment
проверьте этот пост: stackoverflow.com/a/15472594/4516174   -  person yongsunCN    schedule 09.10.2015


Ответы (3)


Возможно, вам лучше создать отдельную (Intent)Service и периодически вызывая его с помощью postDelayed. Создайте BroadcastReceiver в своей деятельности и обработайте там изменения пользовательского интерфейса.

Еще один совет по обработке изменений пользовательского интерфейса из других потоков: это невозможно. Поэтому вам нужно вызвать runOnUiThread. вот как это использовать

person AlbAtNf    schedule 09.10.2015
comment
Служба намерений - это вариант, но есть две причины, по которым я не хочу его использовать. Во-первых, мне не нужно выполнять задачу, пока приложение находится в фоновом режиме, что является основной причиной использования IntentService. Во-вторых, мне нужно что-то, что само запускается периодически, и я думаю, что это можно сделать более простым способом. Спасибо за ответ в любом случае. - person varunkr; 09.10.2015

Вы можете использовать асинхронные задачи. Для этого предназначены:

http://developer.android.com/reference/android/os/AsyncTask.html

Это позволяет вам выполнить сетевой вызов в фоновом режиме, а затем, когда вы получите результат, выполнить действие в потоке пользовательского интерфейса.

Декларация:

 private class MyTask extends AsyncTask<Input, Void, Output> {
 protected Output doInBackground(Input... inputs) {
       // do something on the network
       return myOutput;// use this to transmit your result
 }

 protected void onPostExecute(Output result) {
     // do something on UI thread with the result
 }

}

Если вы хотите повторить это, просто создайте runnable для его запуска и после каждого вызова запланируйте следующий:

MyTask myTask;
Handler handler = new Handler();

    Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                MyTask myTask = new MyTask();
                myTask.execute(myArg);
                handler.postDelayed(netRunnable, 60000); // schedule next call
            }
        }

Чтобы запустить его в первый раз:

handler.postDelayed(myRunnable, 60000);

Или, если вы хотите запустить его немедленно:

handler.post(myRunnable);

Не забудьте отменить задачу, когда ваша активность будет уничтожена:

myTask.cancel(true);
person Yves Delerm    schedule 09.10.2015
comment
Что, если вы повернете телефон во время работы asyncTask? - person AlbAtNf; 09.10.2015
comment
Это зависит от того, инициировано ли событие изменения конфигурации. Если это так, действие уничтожается, и AsyncTask необходимо отменить. Если нет изменения конфигурации, активность не уничтожается, поэтому я думаю, что все в порядке. - person Yves Delerm; 09.10.2015
comment
Если я посмотрю на это, я думаю, что задачу не так просто отменить. Вы также можете потерять ссылку на действительное действие. Отмена сетевой задачи также может быть не лучшей идеей. - person AlbAtNf; 09.10.2015
comment
Я не думаю, что это действительно то, что означает ссылка. Отменит ли это что-либо на самом деле, зависит от того, что вы делаете. Вы по-прежнему можете проверить, была ли задача отменена. Также, если вы этого хотите, сетевая задача может продолжаться сама по себе. - person Yves Delerm; 09.10.2015
comment
Что я имею в виду: нельзя просто вызвать myTask.cancel(true) и задача автоматически остановится. Вы должны обработать это условие в doInBackground Задачи. И поскольку вы можете потерять ссылку на свою активность, вы также должны проверить это. Просто некоторые моменты, о которых вы должны знать при использовании AsyncTasks. - person AlbAtNf; 09.10.2015
comment
Это не точно. При правильном обращении вы никогда не потеряете (и не потеряете) ссылку на свою активность. - person Yves Delerm; 09.10.2015
comment
Как я уже говорил, и это объясняется в ссылке, которую вы разместили: Отменит ли это что-либо на самом деле, зависит от того, что вы делаете. Пожалуйста, не путайте фоновую задачу и задачу пользовательского интерфейса. Если вы хотите отменить фоновую задачу, вы должны регулярно проверять ее. В любом случае, если вы вызываете cancel(), метод PostExecute() не будет вызываться, поэтому потенциальная потеря ссылки на активность не опасна. - person Yves Delerm; 09.10.2015
comment
@YvesDelerm Спасибо за ответ, но во время выполнения этих задач может происходить переключение действий. Таким образом, я не думаю, что AsyncTask будет правильным решением в этом случае! - person varunkr; 09.10.2015
comment
Хорошо я понял. Мой ответ больше для общих случаев. Возможно, вам следует уточнить это в своем вопросе, чтобы получить более конкретные ответы. - person Yves Delerm; 09.10.2015

Если действия часто меняются, почему бы не поменять местами обязанности. Вы можете создать службу, которая выполняет периодическую сетевую задачу.

Затем - либо ваша деятельность периодически вызывает эту службу, чтобы получить значение. - или вы используете систему прослушивания: вы создаете интерфейс, который должны реализовать ваши действия, чтобы получать уведомления о завершении задачи

person Yves Delerm    schedule 09.10.2015