¿Cómo puedo ejecutar código en un hilo de fondo en Android?

131

Quiero que algún código se ejecute en segundo plano continuamente. No quiero hacerlo en un servicio. ¿Hay alguna otra forma posible?

He intentado llamar a la Threadclase en mi Activitypero mis Activityrestos permanecen en segundo plano por algún tiempo y luego se detiene. La Threadclase también deja de funcionar.

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

    }
usuario2082987
fuente
1
Hola. ¿Puede publicar algún código como ejemplo de lo que ha intentado? Existen diferentes formas de ejecutar código en un subproceso en segundo plano, pero debe recordar que si está accediendo a algún componente de la interfaz de usuario, debe acceder a ellos en el subproceso de la interfaz de usuario mediante el runOnUiThread()método.
Alex Wiese
3
¿Hay alguna razón en particular para no usar el Servicio
DeltaCap019
Esto no es tan recomendable. funcionando continuamente agotará la batería del dispositivo.
HelmiB
He enviado el código.
user2082987
Intenté usarlo en el servicio pero está enviando el mismo registro varias veces.
user2082987

Respuestas:

379

Si lo necesitas:

  1. ejecutar código en un hilo de fondo

  2. ejecutar código que NO toca / actualiza la IU

  3. ejecutar código (corto) que tardará como máximo unos segundos en completarse

ENTONCES use el siguiente patrón limpio y eficiente que usa AsyncTask:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});
Brandon Rude
fuente
10
Nota: Requiere API nivel 11
friederbluemle
9
¿Por qué no usaría lo siguiente, que es más liviano: nuevo subproceso (nuevo Runnable () {@Override public void run () {// código de fondo}}). Start ();
awardak
3
¿Existe la posibilidad de que esto cause pérdida de memoria?
Hilfritz
55
Una cosa a tener en cuenta es que AsyncTasks se qeued. Si usa esto para un montón de llamadas de API de servidor, no se ejecutarán en paralelo.
Chase Roberts
45

Recuerde Ejecutar en segundo plano, Ejecutar continuamente son dos tareas diferentes.

Para procesos en segundo plano a largo plazo, los hilos no son óptimos con Android. Sin embargo, aquí está el código y hágalo bajo su propio riesgo ...

Recuerde que Service o Thread se ejecutará en segundo plano, pero nuestra tarea debe activarse (llamar una y otra vez) para obtener actualizaciones, es decir, una vez que se complete la tarea, debemos recuperar la función para la próxima actualización.

Temporizador (activador periódico), Alarma (activador de base de tiempo), Difusión (Activador de base de eventos), la recursividad despertará nuestras funciones.

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

Uso del servicio: si inicia un servicio, se iniciará, ejecutará la tarea y finalizará. después de la ejecución de la tarea. terminado también puede ser causado por una excepción, o el usuario lo eliminó manualmente desde la configuración. START_STICKY (Sticky Service) es la opción dada por Android de que el servicio se reiniciará solo si el servicio finaliza.

¿Recuerdas la diferencia de preguntas entre multiprocesamiento y multiproceso? El servicio es un proceso en segundo plano (al igual que la actividad sin interfaz de usuario), de la misma manera que inicia el hilo en la actividad para evitar la carga en el hilo principal (hilo de actividad), de la misma manera que necesita lanzar hilos (o tareas asíncronas) en el servicio para evitar la carga en el servicio.

En una sola declaración, si desea ejecutar una tarea continua en segundo plano, debe iniciar un StickyService y ejecutar el hilo en el servicio en la base de eventos

Sandeep P
fuente
¿Recomendaría este método para ejecutar un SignalStrengthListener personalizado en segundo plano durante un promedio de 20 minutos, mientras toma valores del oyente una vez por segundo para actualizar el hilo de la interfaz de usuario? Aquí hay más contexto: stackoverflow.com/questions/36167719/…
JParks el
¿Cómo se ejecuta el proceso nuevamente si configura 'isRecursionEnable = true'? Luego llama dentro del método 'run' para 'runInBackground ()', ¿cómo no ingresaría la instrucción if al comienzo de la función?
Mickey
23

Quiero que algún código se ejecute en segundo plano continuamente. No quiero hacerlo en un servicio. ¿Hay alguna otra forma posible?

El mecanismo más probable que está buscando es AsyncTask. Se designa directamente para realizar un proceso en segundo plano en el subproceso en segundo plano. Además, su principal beneficio es que ofrece algunos métodos que se ejecutan en el subproceso principal (UI) y hacen posible actualizar su interfaz de usuario si desea anunciar al usuario sobre algún progreso en la tarea o actualizar la interfaz de usuario con los datos recuperados del proceso en segundo plano.

Si no sabes cómo comenzar aquí, es un buen tutorial:

Nota: También existe la posibilidad de usar IntentServicecon ResultReceivereso funciona también.

Simon Dorociak
fuente
@ user2082987, ¿lo intentaste?
Simon Dorociak
estos parámetros de asyncTask son confusos
user2082987
De todos modos, AsyncTask es solo una envoltura alrededor del hilo, por lo que no resuelve el problema de que Android elimine la aplicación mientras está en segundo plano
Lassi Kinnunen
Hola @LassiKinnunen. Escribí esa respuesta hace 5 años. Ahora todo ha cambiado y actualmente no estoy usando AsyncTask porque su pérdida de memoria no es segura.
Simon Dorociak
Sí, me doy cuenta de eso, pero la gente todavía encontrará esto a través de Google y he visto muchos comentarios de personas que recomiendan usar los envoltorios de Google, incluso cuando tiene poco o ningún propósito, lo siento. En cuanto a la pregunta original si alguien está buscando algunas respuestas en 2018, cree un servicio y ejecútelo y márquelo como en primer plano con una notificación si desea maximizar las posibilidades de que no sea asesinado. El proceso real de ejecución prolongada puede manejarse con controladores o un hilo separado. Sin embargo, ¿no se dieron cuenta de que lograron hacer que no fuera segura la pérdida de memoria?
Lassi Kinnunen
16

Simple 3-Liner

Una forma simple de hacerlo que encontré como un comentario de @awardak en la respuesta de Brandon Rude :

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

No estoy seguro de si, o cómo, esto es mejor que usarlo, AsyncTask.executepero parece funcionar para nosotros. Cualquier comentario sobre la diferencia sería apreciado.

Gracias, @awardak !

Joshua Pinter
fuente
Utilizamos el método handler.post () para publicarlo nuevamente en el hilo principal dentro del método de ejecución de Thread.
androidXP
2

Una alternativa a AsyncTask es robospice. https://github.com/octo-online/robospice .

Algunas de las características de robospice.

1.ejecuta asincrónicamente (en un servicio Android de fondo) solicitudes de red (por ejemplo: solicitudes REST usando Spring Android). Notifique su aplicación, en el hilo de la interfaz de usuario, cuando el resultado esté listo.

2.es fuertemente tipado! Realiza sus solicitudes utilizando POJO y obtiene POJO como resultados de la solicitud.

3. no imponga restricciones ni a los POJO utilizados para las solicitudes ni a las clases de Actividad que utilice en sus proyectos.

4.caché resultados (en Json con Jackson y Gson, o Xml, o archivos de texto plano, o archivos binarios, incluso usando ORM Lite).

5. notifica sus actividades (o cualquier otro contexto) del resultado de la solicitud de red si y solo si todavía están vivos

6.no hay pérdida de memoria, como Android Loaders, a diferencia de Android AsyncTasks notifica tus actividades en su UI Thread.

7.utiliza un modelo de manejo de excepciones simple pero robusto.

Muestras para empezar. https://github.com/octo-online/RoboSpice-samples .

Una muestra de robospice en https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result .

Raghunandan
fuente
¿Cómo almacenar una matriz obtenida de la red a SQLite? Básicamente me gustaría: obtener datos de la red Y guardarlos en mi SQLite, tanto en el hilo de fondo, luego recibir mi UI para actualizar ListView. Normalmente lo hago con IntentService, pero RoboSpice escribe menos.
Yar
@yar puede usar un servicio de intención si es una operación de larga duración. También puede usar asynctask pero solo si es por un período corto. Puedes crear un hilo y hacer todas tus cosas allí. No he usado robospice en mucho tiempo. Hay otras bibliotecas de redes como volley ...
Raghunandan
OK gracias. Pensé en usar Robospice, pero parece que no es demasiado bueno (flexible) para mis necesidades. Utilizo IntentService durante mucho tiempo con éxito.
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
fuente
55
Bienvenido a StackOverflow. Las respuestas con solo código en ellas tienden a marcarse para su eliminación ya que son de "baja calidad". Lea la sección de ayuda para responder preguntas y luego considere agregar algunos comentarios a su respuesta.
Graham
0

Si necesita ejecutar un hilo predioticamente con diferentes códigos, este es un ejemplo:

Oyente:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

Clase de hilo

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

Ejecuta diferentes códigos:

  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
fuente