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:
- 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.
- 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.
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