¿Cómo funciona el enhebrado en Android?

Cuando se inicia una aplicación en Android, crea el hilo principal de ejecución , denominado hilo » principal «. La mayoría de los subprocesos son responsables de enviar eventos a los widgets de interfaz aceptables, así como de comunicarse con componentes del kit de herramientas de la interfaz de usuario de Android. Para mantener la capacidad de respuesta de su aplicación , es esencial evitar el uso de la mayoría de los subprocesos para realizar cualquier operación que la mantenga bloqueada.

Las operaciones de red y las llamadas a la base de datos, así como la carga de ciertos componentes, son ejemplos comunes de operaciones que se deben evitar dentro del hilo principal. Una vez que se llaman dentro del subproceso principal, se llaman sincrónicamente, lo que sugiere que la interfaz de usuario no responderá hasta que se complete la operación. Debido a esto, las tareas que requieren llamadas generalmente se realizan en diferentes subprocesos, lo que a su vez evita el bloqueo de la interfaz de usuario y la mantiene receptiva mientras se realizan las tareas. (es decir, se han realizado de forma asincrónica desde la interfaz de usuario).

Android proporciona algunas formas de crear y administrar subprocesos, y existen muchas bibliotecas de terceros que hacen que la administración de subprocesos sea mucho más agradable. Sin embargo, con numerosos enfoques disponibles, elegir el adecuado suele ser bastante confuso. En este artículo, estudiará algunos escenarios comunes en el desarrollo de Android donde los subprocesos se vuelven esenciales y algunas soluciones simples que se aplicarán a esos escenarios y más.

Enhebrar en Android

En Android, categorizará todos los componentes de subprocesamiento en dos categorías básicas:

  1. Subprocesos que están adjuntos a una actividad/fragmento : estos subprocesos están vinculados al ciclo de vida de la actividad/fragmento y finalizan tan pronto como la actividad/fragmento se destruye.
  2. Subprocesos que no están adjuntos a ninguna actividad/fragmento : estos subprocesos aún pueden ejecutarse más allá de la vida útil de la actividad/fragmento (si corresponde) a partir del cual se generaron.

Tipo n.º 1: componentes de subprocesos que se adjuntan a una actividad/fragmento

1. ASYNCTAREA

AsyncTask es el componente de Android más elemental para subprocesos. Es súper fácil y simple de usar, también es bueno para algunos escenarios básicos. 

Java

public class GeeksActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        // Adding Task to the List (Async)
        new MyTask().execute(url);
    }
 
    private class MyTask
        extends AsyncTask<String, Void, String> {
 
        @Override
        protected String doInBackground(String... params)
        {
            String url = params[0];
            return doSomeWork(url);
        }
 
        @Override
        protected void onPostExecute(String result)
        {
            super.onPostExecute(result);
            // do something with the result
        }
    }
}

AsyncTask , sin embargo, se queda corto si desea que su tarea diferida se ejecute más allá de la vida útil de la actividad/fragmento. ¡El hecho de que incluso la más mínima rotación de la pantalla pueda causar que la actividad se destruya es simplemente horrible! Entonces llegamos a:

2. CARGADORES 

Los cargadores son la respuesta a la terrible pesadilla mencionada anteriormente. Los cargadores son excelentes para funcionar en ese contexto y se detienen automáticamente cuando se destruye la actividad, aún más, el dulce hecho es que también se reinician después de que se recrea la actividad. 

Java

public class GeeksActivity extends Activity{
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Getting instance of loader manager
        getLoaderManager().initLoader(1, null, new MyLoaderCallbacks());
    }
     
    private class MyGeekyLoaderCallbacks implements LoaderManager.LoaderCallbacks {
        // Overriding the method
        @Override
        public Loader onCreateLoader(int id, Bundle args) {
            return new MyLoader(GeeksforGeeks.this);
        }
 
        @Override
        public void onLoadFinished(Loader loader, Object data) {
 
        }
 
        @Override
        public void onLoaderReset(Loader loader) {
 
        }
    }
 
    private class MyLoader extends AsyncTaskLoader {
 
        public MyLoader(Context context) {
            super(context);
        }
 
        @Override
        public Object loadInBackground() {
            return someWorkToDo();
        }
         
    }
}

Tipo 2. Enhebrar componentes que no se adjuntan a una actividad/fragmento

1. SERVICIO

El servicio podría considerarse como un componente útil para realizar operaciones largas (o potencialmente largas) sin interfaz de usuario. ¡Sí, oíste bien! ¡Los servicios no tienen ninguna interfaz de usuario de ellos! El servicio se ejecuta dentro del hilo principal de su proceso de hospedaje; el servicio no crea su propio subproceso y no se ejecuta durante un proceso separado a menos que especifique lo contrario. 

Java

public class ExampleServiceGeeks extends Service {
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        doSomeLongProccesingWork();
        stopSelf();
        // Self stopping the service
          // by calling stopSelf();
        return START_NOT_STICKY;
    }
 
    @Nullable
    @Override
    // Binding the service to the Method calls
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Un error de diseño típico

Mira el fragmento de código a continuación:

Java

public class GeeksforGeeks extends Activity {
  // ...
  public class MyAsyncTask extends AsyncTask<Void, Void, String>   {
    @Override protected void onPostExecute(String result) {...}
    @Override protected String doInBackground(Void... params) {...}
  
  }
}

Kotlin

class GeeksforGeeks : Activity() {
    // ...
    inner class MyAsyncTask : AsyncTask<Unit, Unit, String>() {
          override fun onPostExecute(result: String) {...}
        override fun doInBackground(vararg params: Unit): String {...}
    }
}

¿Qué parece mal? 

El error que ocurrió durante este fragmento es que el código declara el objeto de subprocesamiento MyAsyncTask como una clase interna no estática de alguna actividad (o una clase interna en Kotlin). Esta declaración crea una relación implícita con la instancia de Actividad adjunta. Como resultado, el objeto contiene relación con la actividad hasta que se completa el trabajo subproceso, lo que provoca un retraso en la destrucción de la actividad a la que se hace referencia. Por lo tanto, obtenemos un retraso, que a su vez daña el sistema y supone una gran carga para la memoria. Una solución directa al problema actual sería definir sus instancias de clase sobrecargadas como clases estáticas o en sus propios archivos, eliminando así la referencia implícita. 

Otra solución sería cancelar y empaquetar siempre las tareas en segundo plano dentro de la devolución de llamada del ciclo de vida de la actividad adecuada, como onDestroy. Sin embargo, este enfoque suele ser tedioso y propenso a errores. Como regla general, no debe poner lógica compleja que no sea de interfaz de usuario directamente en las actividades. Además, AsyncTask ahora está en desuso y, sin embargo, no se recomienda su uso en código nuevo.

Tarea prioritaria 

Como se describe en Procesos y, por lo tanto, en el Ciclo de vida de la aplicación, la prioridad que reciben los subprocesos de su aplicación depende en parte de dónde se encuentre la aplicación dentro del ciclo de vida de la aplicación. A medida que crea y administra subprocesos en su aplicación, es importante alinear su prioridad para que los subprocesos adecuados obtengan las prioridades adecuadas en los momentos adecuados. 

Si la prioridad es demasiado alta, entonces ese subproceso podría interrumpir el subproceso de la interfaz de usuario e incluso bloquearlo en algunos casos adversos e incluso el subproceso de procesamiento, lo que provocaría problemas de rendimiento de la aplicación, como fotogramas caídos, retraso, interfaz de usuario lenta, etc.  

Cada vez que crea un hilo, debe llamar a setThreadPriority() . El programador de subprocesos del sistema da preferencia a los subprocesos con prioridades altas, equilibrando esas prioridades con la necesidad de terminar todo el trabajo. En general, los subprocesos dentro del grupo de primer plano obtienen alrededor del 95 % del tiempo total de ejecución del dispositivo, mientras que el grupo de fondo obtiene aproximadamente el 5 %.

Por lo tanto, si se está volviendo loco haciendo un trabajo prolongado en los píxeles, esta podría ser una solución mucho mejor para usted. Cuando su aplicación crea un subproceso con HandlerThread , no olvide alinear la prioridad del subproceso compatible con el tipo de trabajo que está realizando. Recuerde, las CPU solo pueden manejar una pequeña cantidad de subprocesos en paralelo. Establecer la prioridad ayuda al sistema a conocer las formas adecuadas de programar este trabajo cuando todos los demás subprocesos luchan por la atención. 

Cola de mensajes que explica el proceso del subproceso

Se puede encontrar una discusión detallada sobre «Enlace de servicio y subprocesos» aquí como referencia.

Publicación traducida automáticamente

Artículo escrito por therebootedcoder y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *