Как запустить код в фоновом потоке на Android?

131

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

Я попытался вызвать Threadкласс в моем, Activityно мое время Activityостается в фоновом режиме, а затем он останавливается. ThreadКласс также перестает работать.

class testThread implements Runnable {
        @Override
        public void run() {
            File file = new File( Environment.getExternalStorageDirectory(), "/BPCLTracker/gpsdata.txt" );
            int i = 0;

            RandomAccessFile in = null;

            try {
                in = new RandomAccessFile( file, "rw" );
            } catch (FileNotFoundException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            }
//String line =null;
            while ( true ) {
                HttpEntity entity = null;
                try {
                    if ( isInternetOn() ) {
                        while ( ( line = in.readLine() ) != null ) {

                            HttpClient client = new DefaultHttpClient();
                            String url = "some url";
                            HttpPost request = new HttpPost( url );
                            StringEntity se = new StringEntity( line );
                            se.setContentEncoding( "UTF-8" );
                            se.setContentEncoding( new BasicHeader( HTTP.CONTENT_TYPE, "application/json" ) );
                            entity = se;
                            request.setEntity( entity );
                            HttpResponse response = client.execute( request );
                            entity = response.getEntity();
                            i++;
                        }
                        if ( ( line = in.readLine() ) == null && entity != null ) {
                            file.delete();
                            testThread t = new testThread();
                            Thread t1 = new Thread( t );
                            t1.start();
                        }


                    } else {
                        Thread.sleep( 60000 );
                    } // end of else

                } catch (NullPointerException e1) {
                    e1.printStackTrace();
                } catch (InterruptedException e2) {
                    e2.printStackTrace();
                } catch (IOException e1) {
// TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }// end of while
        }// end of run

    }
user2082987
источник
1
Здравствуй. Не могли бы вы опубликовать какой-нибудь код в качестве примера того, что вы пробовали? Существуют разные способы запуска кода в фоновом потоке, но вы должны помнить, что если вы обращаетесь к каким-либо компонентам пользовательского интерфейса, вы должны обращаться к ним в потоке пользовательского интерфейса с помощью runOnUiThread()метода.
Alex Wiese
3
Есть ли какая-то конкретная причина для отказа от Сервиса
DeltaCap019
это не рекомендуется. непрерывная работа разрядит аккумулятор устройства.
HelmiB
Я отправил код.
user2082987
Я пробовал использовать его в сервисе, но он отправлял одну и ту же запись несколько раз.
user2082987

Ответы:

379

Если вам нужно:

  1. выполнить код в фоновом потоке

  2. выполнить код, который НЕ касается / не обновляет пользовательский интерфейс

  3. выполнить (короткий) код, выполнение которого займет не более нескольких секунд

ЗАТЕМ используйте следующий чистый и эффективный шаблон, который использует AsyncTask:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});
Брэндон Руд
источник
10
Примечание: Требуется уровень API 11
Friederbluemle
9
Почему бы вам не использовать следующее, более легкое: new Thread (new Runnable () {@Override public void run () {// фоновый код}}). Start ();
Awardak
3
Может ли это вызвать утечку памяти?
Hilfritz
5
Следует отметить, что AsyncTasks поставлены в очередь. Если вы используете это для множества вызовов API сервера, они не будут выполняться параллельно.
Chase Roberts
4
Примечание: AsyncTask устарел на уровне API R
Сандре
45

Помните, что «Запуск в фоновом режиме» и «Постоянный запуск» - это две разные задачи.

Для длительных фоновых процессов потоки не оптимальны для Android. Однако вот код и делайте это на свой страх и риск ...

Помните, что служба или поток будут работать в фоновом режиме, но наша задача должна запускать триггер (вызывать снова и снова), чтобы получать обновления, то есть после завершения задачи нам нужно вызвать функцию для следующего обновления.

Таймер (периодический триггер), Будильник (триггер по временной шкале), Трансляция (Триггер по событию), рекурсия разбудят наши функции.

public static boolean isRecursionEnable = true;

void runInBackground() {
    if (!isRecursionEnable)
        // Handle not to start multiple parallel threads
        return;

    // isRecursionEnable = false; when u want to stop
    // on exception on thread make it true again  
    new Thread(new Runnable() {
        @Override
        public void run() {
            // DO your work here
            // get the data
            if (activity_is_not_in_background) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // update UI
                        runInBackground();
                    }
                });
            } else {
                runInBackground();
            }
        }
    }).start();
}

Использование службы: если вы запустите службу, она запустится, она выполнит задачу и завершится сама собой. после выполнения задания. Прекращение может также быть вызвано исключением, или пользователь убил его вручную из настроек. START_STICKY (Sticky Service) - это опция, предоставляемая android, при которой служба перезапускается, если служба прекращается.

Помните вопрос о разнице между многопроцессорностью и многопоточностью? Служба - это фоновый процесс (точно так же, как активность без пользовательского интерфейса). Так же, как вы запускаете поток в действии, чтобы избежать нагрузки на основной поток (поток активности), точно так же, как вам нужно запускать потоки (или асинхронные задачи) в службе чтобы избежать нагрузки на сервис.

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

Сандип П.
источник
Вы бы порекомендовали этот метод для запуска настраиваемого SignalStrengthListener в фоновом режиме в среднем в течение 20 минут, получая значения от слушателя один раз в секунду для обновления потока пользовательского интерфейса? Вот еще один контекст: stackoverflow.com/questions/36167719/…
JParks
Как процесс снова запустится, если вы установите isRecursionEnable = true? Затем вы вызываете внутри метода run для runInBackground (), как бы он не вводил оператор if в начале функции?
Микки
23

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

Скорее всего механизм, который вы ищете, есть AsyncTask. Он непосредственно предназначен для выполнения фонового процесса в фоновом потоке. Также его основным преимуществом является то, что он предлагает несколько методов, которые работают в основном (UI) потоке и позволяют обновлять ваш пользовательский интерфейс, если вы хотите сообщить пользователю о каком-то прогрессе в задаче или обновить пользовательский интерфейс данными, полученными из фонового процесса.

Если вы не знаете, с чего начать, вот хороший урок:

Примечание: также есть возможность использовать IntentServiceс ResultReceiverэтим тоже работает.

Саймон Дорочак
источник
@ user2082987 так ты пробовал?
Саймон Дорочак
эти параметры asyncTask сбивают с толку
user2082987
В любом случае AsyncTask - это просто оболочка вокруг потока, поэтому он не решает проблему, когда Android убивает приложение, когда оно находится в фоновом режиме,
Ласси Киннунен
Привет, @LassiKinnunen. Я написал этот ответ 5 лет назад. Теперь все изменилось, и я в настоящее время не использую AsyncTask, потому что его утечка памяти небезопасна.
Саймон Дорочак
Да, я понимаю, но люди все равно найдут это через Google, и я видел так много комментариев людей, рекомендующих использовать обертки Google, даже когда это мало или бесполезно, извините. Что касается исходного вопроса, если кто-то ищет ответы в 2018 году, создайте службу, запустите ее и отметьте ее как находящуюся на переднем плане с уведомлением, если вы хотите максимизировать шансы на то, что ее не убьют. Фактически длительный процесс можно обрабатывать с помощью обработчиков или отдельного потока. Неужели не понимали, что им удалось сделать это не безопасным для утечки памяти?
Lassi Kinnunen
16

Простой 3-слойный

Простой способ сделать это, который я нашел в комментарии @awardak в ответе Брэндона Руда :

new Thread( new Runnable() { @Override public void run() { 
  // Run whatever background code you want here.
} } ).start();

Я не уверен, лучше ли это и как это использовать, AsyncTask.executeно, похоже, у нас это работает. Будем признательны за любые комментарии относительно разницы.

Спасибо, @awardak !

Джошуа Пинтер
источник
мы используем метод handler.post (), чтобы отправить его обратно в основной поток внутри метода запуска Thread.
androidXP
2

Альтернативой AsyncTask является robospice. https://github.com/octo-online/robospice .

Некоторые особенности robospice.

1. выполняет асинхронно (в фоновом режиме AndroidService) сетевые запросы (например, запросы REST с использованием Spring Android). Уведомляет ваше приложение в потоке пользовательского интерфейса, когда результат будет готов.

2. строго типизирован! Вы делаете свои запросы с помощью POJO и получаете POJO в качестве результатов запроса.

3. не применять никаких ограничений ни для POJO, используемых для запросов, ни для классов Activity, которые вы используете в своих проектах.

4. кэширует результаты (в Json и с Jackson и Gson, или с Xml, или с плоскими текстовыми файлами, или с двоичными файлами, даже с использованием ORM Lite).

5. уведомляет ваши действия (или любой другой контекст) о результате сетевого запроса, если и только если они все еще живы

6. вообще нет утечки памяти, как Android Loaders, в отличие от Android AsyncTasks уведомляет о ваших действиях в своем потоке пользовательского интерфейса.

7. использует простую, но надежную модель обработки исключений.

Образцы для начала. https://github.com/octo-online/RoboSpice-samples .

Образец robospice на https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result .

Raghunandan
источник
Как сохранить массив, полученный из сети, в SQLite? В основном я хотел бы: получить данные из сети И сохранить их в моем SQLite - как в фоновом потоке, так и получить уведомление о моем пользовательском интерфейсе для обновления ListView. Обычно я делаю это с помощью IntentService, но RoboSpice меньше печатает.
Яр
@yar вы можете использовать службу намерения, если это длительная операция. Также вы можете использовать asynctask, но только на короткое время. Вы можете создать ветку и делать там все свои дела. Давно не пользуюсь robospice. Есть и другие сетевые библиотеки, например, volley ...
Raghunandan
Хорошо, спасибо. Я думал об использовании Robospice, но похоже, что он не слишком хорош (гибок) для моих нужд. Давно пользуюсь IntentService.
Яр
2
class Background implements Runnable {
    private CountDownLatch latch = new CountDownLatch(1);
    private  Handler handler;

    Background() {
        Thread thread = new Thread(this);
        thread.start();
        try {
            latch.await();
        } catch (InterruptedException e) {
           /// e.printStackTrace();
        }
    }

    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler();
        latch.countDown();
        Looper.loop();
    }

    public Handler getHandler() {
        return handler;
    }
}
Гено Папашвили
источник
5
Добро пожаловать в StackOverflow. Ответы, содержащие только код, обычно помечаются для удаления, так как они «низкого качества». Пожалуйста, прочтите раздел справки по ответам на вопросы, а затем подумайте о добавлении комментариев к вашему ответу.
Грэм
0

Если вам нужно предварительно запустить поток с разными кодами, вот пример:

Слушатель:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

Класс потока

public class ESLThread extends AsyncTask<Void, Void, List> {


    private ESLThreadListener mListener;

    public ESLThread() {

        if(mListener != null){

            mListener.onBackground();
        }
    }

    @Override
    protected List doInBackground(Void... params) {

        if(mListener != null){

            List list = mListener.onBackground();

            return list;
        }

        return null;
    }

    @Override
    protected void onPostExecute(List t) {
        if(mListener != null){

            if ( t != null) {
                mListener.onPostExecute(t);
            }
        }

    }


    public void setListener(ESLThreadListener mListener){

        this.mListener = mListener;
    }
}

Запускаем разные коды:

  ESLThread thread = new ESLThread();
                        thread.setListener(new ESLThreadListener() {
                            @Override
                            public List onBackground() {
                                List<EntityShoppingListItems>  data = RoomDB.getDatabase(context).sliDAO().getSL(fId);

                                return data;

                            }

                            @Override
                            public void onPostExecute(List t) {

                                List<EntityShoppingListItems> data = (List<EntityShoppingListItems>)t;
                                adapter.setList(data);
                            }
                        });

                        thread.execute();
Ucdemir
источник