Jak mogę uruchomić kod w wątku w tle w systemie Android?


136

Chcę, aby jakiś kod działał w tle w sposób ciągły. Nie chcę tego robić w serwisie. Czy jest inny sposób?

Próbowałem zadzwonić do Threadklasy w moim, Activityale Activityprzez jakiś czas pozostaje w tle, a potem to się zatrzymuje. ThreadKlasa przestaje również działać.

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

    }

1
Cześć. Czy możesz wysłać kod jako przykład tego, co wypróbowałeś? Istnieją różne sposoby uruchamiania kodu w wątku w tle, ale musisz pamiętać, że jeśli uzyskujesz dostęp do jakichkolwiek składników interfejsu użytkownika, musisz uzyskać do nich dostęp w wątku interfejsu użytkownika przy użyciu tej runOnUiThread()metody.
Alex Wiese

3
Czy jest jakiś szczególny powód, aby nie korzystać z usługi
DeltaCap019

nie jest to zalecane. ciągła praca spowoduje rozładowanie baterii urządzenia.
HelmiB

Wysłałem kod.
user2082987

Próbowałem go używać w serwisie, ale wielokrotnie wysyłał ten sam rekord.
user2082987

Odpowiedzi:


386

Jeśli potrzebujesz:

  1. wykonać kod w tle Thread

  2. wykonać kod, który NIE DOTYKA / aktualizuje interfejsu użytkownika

  3. wykonuje (krótki) kod, który zajmie najwyżej kilka sekund

Następnie użyj następującego czystego i wydajnego wzorca, który używa AsyncTask:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});

10
Uwaga: Wymaga poziomu API 11
friederbluemle

12
Dlaczego nie miałbyś użyć poniższego, który jest lżejszy: new Thread (new Runnable () {@Override public void run () {// kod w tle}}). Start ();
Awardak

3
Czy istnieje możliwość, że spowoduje to wyciek pamięci?
Hilfritz

5
Należy zwrócić uwagę na to, że AsyncTasks są qeued. Jeśli użyjesz tego do kilku wywołań API serwera, nie będą one działać równolegle.
Chase Roberts


45

Pamiętaj: Running Background, Running Continuous to dwa różne zadania.

W przypadku długoterminowych procesów w tle wątki nie są optymalne w systemie Android. Jednak oto kod i zrób to na własne ryzyko ...

Pamiętaj, że Usługa lub Wątek będzie działać w tle, ale nasze zadanie musi wywołać wyzwalacz (dzwonić wielokrotnie), aby uzyskać aktualizacje, tj. Po zakończeniu zadania musimy przywołać funkcję do następnej aktualizacji.

Timer (wyzwalanie okresowe), alarm (wyzwalanie podstawy czasu), rozgłaszanie (wyzwalanie podstawy zdarzenia), rekurencja budzi nasze funkcje.

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();
}

Korzystanie z usługi: Jeśli uruchomisz usługę, uruchomi się, wykona zadanie i zakończy się. po wykonaniu zadania. przerwane może być również spowodowane przez wyjątek lub użytkownik zabił go ręcznie z ustawień. START_STICKY (Sticky Service) to opcja podana przez Androida, która uruchomi się ponownie, jeśli usługa zostanie zakończona.

Pamiętasz kwestię różnicy między przetwarzaniem wieloprocesowym a wielowątkowością? Usługa jest procesem w tle (podobnie jak działanie bez interfejsu użytkownika), w ten sam sposób, w jaki uruchamiasz wątek w działaniu, aby uniknąć obciążenia głównego wątku (wątek działania), w taki sam sposób, w jaki musisz uruchamiać wątki (lub zadania asynchroniczne) w usłudze aby uniknąć obciążenia usług.

W jednej instrukcji, jeśli chcesz uruchomić zadanie kontynuowania w tle, musisz uruchomić StickyService i uruchomić wątek w usłudze na bazie zdarzeń


Czy poleciłbyś tę metodę do uruchamiania niestandardowego SignalStrengthListener w tle przez średnio 20 minut, jednocześnie pobierając wartości z odbiornika raz na sekundę w celu zaktualizowania wątku interfejsu użytkownika? Tutaj jest więcej kontekstu: stackoverflow.com/questions/36167719/…
JParks

Jak proces przebiega ponownie, jeśli ustawisz „isRecursionEnable = true”? Następnie wywołujesz wewnątrz metody „run” dla „runInBackground ()”, w jaki sposób nie wprowadziłby on instrukcji if na początku funkcji?
Mickey

23

Chcę, aby jakiś kod działał w tle w sposób ciągły. Nie chcę tego robić w serwisie. Czy jest inny sposób?

Najprawdopodobniej mechanizm, którego szukasz, to AsyncTask. Jest bezpośrednio przeznaczony do wykonywania procesów w tle w wątku w tle. Jego główną zaletą jest również to, że oferuje kilka metod, które działają w wątku głównym (UI) i umożliwiają aktualizację interfejsu użytkownika, jeśli chcesz powiadomić użytkownika o pewnym postępie w zadaniu lub zaktualizować interfejs użytkownika za pomocą danych pobranych z procesu w tle.

Jeśli nie wiesz, jak zacząć tutaj, to fajny tutorial:

Uwaga: istnieje również możliwość korzystania IntentServicez ResultReceivertego również.


@ user2082987, więc próbowałeś tego?
Simon Dorociak

te parametry asyncTask są mylące
user2082987

AsyncTask i tak to tylko otoka wokół wątku, więc nie rozwiązuje problemu z zabijaniem aplikacji przez Androida, gdy jest w tle
Lassi Kinnunen

Cześć @LassiKinnunen Napisałem tę odpowiedź 5 lat temu. Teraz wszystko się zmieniło i obecnie nie używam AsyncTask, ponieważ wyciek pamięci nie jest bezpieczny.
Simon Dorociak

Tak, zdaję sobie z tego sprawę, ale ludzie nadal znajdą to w Google i widziałem tak wiele komentarzy osób polecających używanie google wrapper, nawet jeśli to niewiele lub bez celu, przepraszam. Jeśli chodzi o pierwotne pytanie, jeśli ktoś szuka odpowiedzi w 2018 roku, utwórz usługę i uruchom ją i oznacz ją jako na pierwszym planie z powiadomieniem, jeśli chcesz zmaksymalizować szanse na to, że nie zostanie zabity. Rzeczywisty, długotrwały proces może być obsługiwany przez programy obsługi lub oddzielny wątek. Nie zdawałeś sobie sprawy, że udało im się sprawić, że wyciek pamięci nie jest bezpieczny?
Lassi Kinnunen

18

Prosty 3-liniowy

Prosty sposób na zrobienie tego, który znalazłem jako komentarz @awardak w odpowiedzi Brandona Rude :

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

Nie jestem pewien, czy i jak to jest lepsze niż używanie, AsyncTask.executeale wydaje się, że działa dla nas. Wszelkie uwagi dotyczące tej różnicy będą mile widziane.

Dzięki, @awardak !


używamy metody handler.post (), aby wysłać ją z powrotem do głównego wątku w metodzie run Thread.
androidXP

2

Alternatywą dla AsyncTask jest Robospice. https://github.com/octo-online/robospice .

Niektóre cechy Robospice.

1. wykonuje asynchronicznie (w tle AndroidService) żądania sieciowe (np. Żądania REST przy użyciu Spring Android). Powiadamiają aplikację w wątku UI, gdy wynik jest gotowy.

2. jest mocno wpisane! Wysyłasz swoje żądania za pomocą POJO i otrzymujesz POJO jako wyniki żądania.

3. nie wymuszaj żadnych ograniczeń ani na POJO używanych do żądań ani na klasach aktywności, których używasz w swoich projektach.

4. zapisuje wyniki w pamięci podręcznej (w Json z Jacksonami i Gsonem lub Xml lub zwykłymi plikami tekstowymi lub plikami binarnymi, nawet używając ORM Lite).

5. powiadamia twoje działania (lub w jakimkolwiek innym kontekście) o wyniku żądania sieciowego wtedy i tylko wtedy, gdy są one nadal żywe

6. nie ma żadnego wycieku pamięci, podobnie jak Android Loaders, w przeciwieństwie do Android AsyncTasks powiadamia o twoich działaniach w swoim wątku interfejsu użytkownika.

7. wykorzystuje prosty, ale solidny model obsługi wyjątków.

Na początek próbki. https://github.com/octo-online/RoboSpice-samples .

Próbka robospice pod adresem https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result .


Jak przechowywać tablicę uzyskaną z sieci w SQLite? Zasadniczo chciałbym: pobrać dane z sieci ORAZ zapisać je w moim SQLite - oba w wątku w tle, a następnie otrzymać powiadomienie o konieczności odświeżenia ListView. Zwykle robię to z IntentService, ale RoboSpice mniej pisze.
Yar

@yar możesz użyć usługi intencji, jeśli jest to długotrwała operacja. Możesz także użyć asynctask, ale tylko wtedy, gdy jest to krótkotrwałe. Możesz utworzyć wątek i tam robić wszystkie swoje rzeczy. Od dawna nie używałem Robospice. Istnieją inne biblioteki sieciowe, takie jak volley ...
Raghunandan

Ok dzięki. Myślałem o użyciu Robospice, ale wygląda na to, że nie jest zbyt dobry (elastyczny) jak na moje potrzeby. Od dłuższego czasu używam IntentService z sukcesem.
Yar

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
Witamy w StackOverflow. Odpowiedzi zawierające tylko kod są zwykle oznaczane do usunięcia, ponieważ są „niskiej jakości”. Przeczytaj sekcję pomocy dotyczącą odpowiadania na pytania, a następnie rozważ dodanie komentarza do swojej odpowiedzi.
Graham

0

Jeśli potrzebujesz uruchomić wątek z różnymi kodami, oto przykład:

Słuchacz:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

Klasa wątku

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;
    }
}

Uruchom różne kody:

  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();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.