Comment puis-je exécuter du code sur un fil d'arrière-plan sur Android?

131

Je veux que du code s'exécute en arrière-plan en continu. Je ne veux pas le faire dans un service. Y a-t-il un autre moyen possible?

J'ai essayé d'appeler la Threadclasse dans mon Activitymais mon Activityreste en arrière-plan pendant un certain temps, puis il s'arrête. La Threadclasse cesse également de fonctionner.

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
la source
1
Salut. Pouvez-vous s'il vous plaît poster un code comme exemple de ce que vous avez essayé? Il existe différentes manières d'exécuter du code sur un thread d'arrière-plan, mais vous devez vous rappeler que si vous accédez à des composants d'interface utilisateur, vous devez y accéder sur le thread d'interface utilisateur à l'aide de la runOnUiThread()méthode.
Alex Wiese
3
Y a-t-il une raison particulière pour ne pas utiliser le service
DeltaCap019
ce n'est pas si recommandé. fonctionner en continu épuisera la batterie de l'appareil.
HelmiB
J'ai envoyé le code.
user2082987
J'ai essayé de l'utiliser en service mais il envoie le même enregistrement plusieurs fois.
user2082987

Réponses:

379

Si tu as besoin de:

  1. exécuter du code sur un thread d'arrière-plan

  2. exécuter du code qui NE touche PAS / ne met pas à jour l'interface utilisateur

  3. exécuter un code (court) qui prendra au plus quelques secondes pour se terminer

ALORS utilisez le modèle propre et efficace suivant qui utilise AsyncTask:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});
Brandon Rude
la source
10
Remarque: nécessite le niveau d'API 11
friederbluemle
9
Pourquoi n'utiliseriez-vous pas ce qui est plus léger: new Thread (new Runnable () {@Override public void run () {// code d'arrière-plan}}). Start ();
awardak
3
Est-il possible que cela provoque une fuite de mémoire?
Hilfritz
5
Une chose à noter est que les AsyncTasks sont mis en file d'attente. Si vous l'utilisez pour un tas d'appels d'API serveur, ils ne fonctionneront pas en parallèle.
Chase Roberts
45

Rappelez-vous Running Background, Running continuellement sont deux tâches différentes.

Pour les processus d'arrière-plan à long terme, les threads ne sont pas optimaux avec Android. Cependant, voici le code et faites-le à vos risques et périls ...

N'oubliez pas que le service ou le thread s'exécutera en arrière-plan, mais notre tâche doit déclencher (appeler encore et encore) pour obtenir les mises à jour, c'est-à-dire qu'une fois la tâche terminée, nous devons rappeler la fonction pour la prochaine mise à jour.

Timer (déclenchement périodique), alarme (déclenchement de base de temps), diffusion (déclenchement de base d'événement), la récursivité réveillera nos fonctions.

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

Utilisation du service: Si vous lancez un service, il démarrera, il exécutera la tâche et se terminera de lui-même. après l'exécution de la tâche. résilié peut également être causé par une exception, ou l'utilisateur l'a tué manuellement à partir des paramètres. START_STICKY (Sticky Service) est l'option donnée par Android selon laquelle le service redémarrera lui-même si le service est interrompu.

Rappelez-vous la différence de question entre le multitraitement et le multithreading? Le service est un processus d'arrière-plan (tout comme l'activité sans interface utilisateur), de la même manière que vous lancez le thread dans l'activité pour éviter la charge sur le thread principal (thread d'activité), de la même manière que vous devez lancer des threads (ou des tâches asynchrones) sur le service pour éviter la charge sur le service.

En une seule instruction, si vous souhaitez exécuter une tâche en arrière-plan, vous devez lancer un StickyService et exécuter le thread dans le service sur la base des événements.

Sandeep P
la source
Recommanderiez-vous cette méthode pour exécuter un SignalStrengthListener personnalisé en arrière-plan pendant 20 minutes en moyenne, tout en récupérant les valeurs de l'écouteur une fois par seconde pour mettre à jour le thread d'interface utilisateur? Voici plus de contexte: stackoverflow.com/questions/36167719/…
JParks
Comment le processus s'exécute-t-il à nouveau si vous définissez 'isRecursionEnable = true'? Vous appelez ensuite à l'intérieur de la méthode 'run' pour 'runInBackground ()', comment n'entrerait-il pas dans l'instruction if au début de la fonction?
Mickey
23

Je veux que du code s'exécute en arrière-plan en continu. Je ne veux pas le faire dans un service. Y a-t-il un autre moyen possible?

Le mécanisme le plus probable que vous recherchez est AsyncTask. Il est directement désigné pour effectuer le processus d'arrière-plan sur le fil d'arrière-plan. Son principal avantage est également qu'il offre quelques méthodes qui s'exécutent sur le fil principal (UI) et permettent de mettre à jour votre interface utilisateur si vous souhaitez annoncer à l'utilisateur la progression de la tâche ou mettre à jour l'interface utilisateur avec des données récupérées à partir du processus d'arrière-plan.

Si vous ne savez pas par où commencer, voici un joli tutoriel:

Remarque: il est également possible d'utiliser IntentServiceavec ResultReceivercela fonctionne également.

Simon Dorociak
la source
@ user2082987 alors l'avez-vous essayé?
Simon Dorociak
ces paramètres de asyncTask sont déroutants
user2082987
AsyncTask est de toute façon juste un wrapper autour du fil de discussion, il ne résout donc pas le problème d'Android qui tue l'application alors qu'elle est en arrière
Lassi Kinnunen
Salut @LassiKinnunen J'ai écrit cette réponse il y a 5 ans. Maintenant, tout est changé et je n'utilise pas actuellement AsyncTask car sa mémoire ne fuit pas en toute sécurité.
Simon Dorociak
Oui, je m'en rends compte, mais les gens trouveront toujours cela sur Google et j'ai vu tellement de commentaires de personnes recommandant d'utiliser les wrappers google, même si cela ne sert pas à grand-chose, voire à rien, désolé. En ce qui concerne la question d'origine si quelqu'un cherche des réponses en 2018, créez un service et lancez-le et marquez-le comme étant au premier plan avec une notification si vous voulez maximiser les chances qu'il ne soit pas tué. Le processus de longue durée réel peut être géré avec des gestionnaires ou un thread séparé. Vous ne saviez pas qu'ils avaient réussi à rendre la mémoire non sûre?
Lassi Kinnunen
16

Simple 3-Liner

Une façon simple de faire cela que j'ai trouvée dans un commentaire de @awardak dans la réponse de Brandon Rude :

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

Je ne sais pas si, ni comment, c'est mieux que d'utiliser, AsyncTask.executemais cela semble fonctionner pour nous. Tout commentaire sur la différence serait apprécié.

Merci, @awardak !

Joshua Pinter
la source
nous utilisons la méthode handler.post () pour le publier sur le thread principal à l'intérieur de la méthode d'exécution de Thread.
androidXP
2

Une alternative à AsyncTask est robospice. https://github.com/octo-online/robospice .

Certaines des fonctionnalités de robospice.

1. exécute de manière asynchrone (dans un AndroidService en arrière-plan) des requêtes réseau (ex: requêtes REST à l'aide de Spring Android) .notifiez votre application, sur le thread de l'interface utilisateur, lorsque le résultat est prêt.

2. est fortement typé! Vous faites vos demandes à l'aide de POJO et vous obtenez des POJO comme résultats de demande.

3. n'appliquez aucune contrainte ni aux POJO utilisés pour les requêtes, ni aux classes d'activité que vous utilisez dans vos projets.

4. met en cache les résultats (dans Json avec Jackson et Gson, ou Xml, ou des fichiers texte plats, ou des fichiers binaires, même en utilisant ORM Lite).

5. notifie vos activités (ou tout autre contexte) du résultat de la requête réseau si et seulement si elles sont toujours actives

6.pas de fuite de mémoire du tout, comme les chargeurs Android, contrairement à Android AsyncTasks notifie vos activités sur leur fil d'interface utilisateur.

7. utilise un modèle de gestion des exceptions simple mais robuste.

Des échantillons pour commencer. https://github.com/octo-online/RoboSpice-samples .

Un échantillon de robospice sur https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result .

Raghunandan
la source
Comment stocker un tableau obtenu du réseau vers SQLite? Fondamentalement, je voudrais: obtenir des données du réseau ET les enregistrer sur mon SQLite - à la fois dans le fil d'arrière-plan, puis recevoir une notification de mon interface utilisateur pour actualiser ListView. Je le fais généralement avec IntentService, mais RoboSpice est moins typé.
Yar
@yar, vous pouvez utiliser un service d'intention s'il s'agit d'une opération de longue durée. Vous pouvez également utiliser asynctask, mais seulement si c'est pour une courte durée. Vous pouvez créer un fil et faire tout votre travail là-bas. Je n'ai pas utilisé robospice depuis longtemps maintenant. Il existe d'autres bibliothèques de réseautage comme volley ...
Raghunandan
OK merci. J'ai pensé à utiliser Robospice, mais il semble que ce n'est pas trop bon (flexible) pour mes besoins. J'utilise IntentService depuis longtemps avec succès.
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;
    }
}
Geno Papashvili
la source
5
Bienvenue dans StackOverflow. Les réponses contenant uniquement du code ont tendance à être signalées pour suppression car elles sont de "mauvaise qualité". Veuillez lire la section d'aide sur les réponses aux questions, puis pensez à ajouter des commentaires à votre réponse.
Graham
0

Si vous avez besoin d'exécuter un thread predioticly avec différents codes, voici un exemple:

Auditeur:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

Classe de fil

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

Exécutez différents codes:

  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
la source