¿Cuál es la diferencia entre controladores y servicios en Android?
En cuanto a componentes y aplicaciones en Android, la mayoría de los anteriores eran conceptos estáticos. Y cuando la aplicación se está ejecutando, inevitablemente es necesario prestar atención a conceptos como procesos y subprocesos. En Android, donde los componentes se ejecutan dinámicamente, uno de los conceptos más distintivos es Tarea, que se traduce como tarea, lo cual debería ser bastante lógico.
La función principal de la intervención de Task es conectar componentes entre sí y separarlos de los detalles de los conceptos del proceso. Puede configurar cosas en diferentes modos, puede simplificar la dificultad de comprensión. de desarrolladores de nivel superior, para ayudar a todos a desarrollar y configurar mejor.
Tarea
En el SDK sobre Tarea (guide/topics/fundamentals.html#acttask), hay una buena metáfora, es decir, Tarea es equivalente a la aplicación (aplicación) . concepto. A los ojos de los desarrolladores, desarrollar programas de Android es crear un componente separado, pero para los usuarios comunes, lo que perciben es solo una aplicación en ejecución como un todo, y detrás de este todo está Task.
En pocas palabras, una Tarea es una colección de componentes de Actividad reunidos en modo pila. Existe una posible conexión de ida y vuelta entre ellos. El componente Actividad recién agregado está en la parte superior de la pila. Solo la Actividad en la parte superior de la pila tiene la oportunidad de interactuar con el usuario. Cuando la actividad en la parte superior de la pila completa su tarea y sale, Task la elimina de la pila y deja la siguiente actividad que se ejecutará en la parte superior de la pila frente al usuario hasta que no haya más actividades en la pila y la Tarea. termina.
Pila de tareas de actividades
Toca la aplicación Correo electrónico para ingresar a la bandeja de entrada (Actividad A) A
Selecciona un correo electrónico y toca para ver los detalles (Actividad B) AB
Toca Responder para comenzar a redactar un nuevo correo electrónico (Actividad C) ABC
Después de escribir algunas líneas, toca para seleccionar un contacto para ingresar a la pantalla "Seleccionar contacto" (Actividad D) ABCD
Selecciona el contacto y continúa redactando el correo electrónico ABC
Redacta el correo electrónico, completa el envío, vuelve al correo electrónico original AB
Haz clic en "Volver" " Volver a la Bandeja de entrada A
Salir del programa de correo electrónico null
Como se muestra en la tabla anterior, es un ejemplo. Desde el momento en que el usuario ingresa al buzón hasta que se completa la respuesta y se cierra la aplicación, la pila de tareas cambia durante todo el proceso. Este es un modelo de pila estándar, que es suficiente para la mayoría de los casos, pero cuando se trata de rendimiento real, gastos generales, etc., se vuelve mucho más brutal. Por ejemplo, en Android, iniciar el navegador es un proceso laborioso que requiere mucho trabajo de inicialización y tiene una gran sobrecarga de memoria. Pero al mismo tiempo, usar un navegador para abrir algo también es un requisito para las aplicaciones generales. Imagínese lo cruel que sería si hubiera diez aplicaciones en ejecución (correspondientes a múltiples tareas) que necesitaran iniciar el navegador: diez pilas de tareas están llenas de actividades de navegador muy similares. Entonces tienes la idea de que la actividad del navegador no puede existir como una tarea separada, no importa cuál sea la solicitud de la tarea, la tarea del navegador no se fusionará. De esta manera, aunque la actividad del navegador en sí necesita mantener más estados, la sobrecarga general se reducirá considerablemente. Este tipo de sacrificio para todos sigue siendo muy encomiable.
Las manos de Android soportan este comportamiento. En Android, el modo de tarea de cada actividad es configurable y seleccionable por el proveedor de la actividad (a través de un archivo de configuración...) y el usuario de la actividad (a través de la información de la marca en el Intent...). Por supuesto, el control del usuario sobre las actividades se limita a lo que permite el proveedor, y el usuario no puede utilizar modos que estén explícitamente prohibidos por el proveedor.
En el SDK (guide/topics/fundamentals.html#acttask), las dos configuraciones para implementar el modo de tarea están escritas con mucha claridad. Extraje algunas explicaciones (elementos de configuración completos, asegúrese de consultar el SDK). , estos son solo algunos elementos de uso común...). El proveedor configura el componente a través del elemento
Para los desarrolladores de aplicaciones, el atributo launchMode en
El modo estándar es el modo de tarea estándar y predeterminado. Sin considerar otros factores, una Actividad que use este modo construirá una instancia de la Actividad y la agregará a la pila de tareas de la persona que llama, entre otras cosas, para las actividades cuyo. La frecuencia de uso y el costo promedio son generalmente los más altos entre todas las cosas, el modo estándar es sin duda el más adecuado. Debido a que tiene una lógica simple y una organización clara, es la opción predeterminada.
El modo SingleTop es básicamente el mismo que el modo estándar, excepto cuando la actividad solicitada está en la parte superior de la pila. En este punto, la actividad configurada como singleTop ya no creará una nueva instancia para agregar a la pila de tareas, sino que enviará el nuevo Intent a la actividad de nivel superior. La actividad de nivel superior puede manejar el nuevo Intent anulando onNewIntent (. por supuesto también puede ignorarlo... ...). Este patrón reduce algunos gastos generales de duplicación cuando se está en la parte superior de la pila y evita algunos comportamientos extraños (imagínese cómo sería si varias actividades consecutivas en la parte superior de la pila fueran todas la misma Actividad y luego salieran una por una) Usuario experiencia...). Por lo tanto, BrowserBox es ideal para actividades que actualizan la visualización de una lista. Como ejemplo vivo, la actividad de marcadores BrowserBookmarkPage en la aplicación predeterminada de Android usa singleTop.
Aunque el modo singleTop rompe la lógica de la pila original (reutilizar la parte superior de la pila sin crear nuevos elementos en la pila), no afecta los nuevos elementos en la pila (los nuevos elementos ingresan a la pila. .. ...). Una actividad marcada como singleTask tiene como máximo una instancia, ubicada dentro de la tarea que tiene su raíz. Todas las solicitudes a esta actividad saltarán a las tareas de la actividad. singleTask es muy similar al patrón singleton conceptual (todas las modificaciones se basan en una sola instancia) y a menudo se usa en actividades donde el costo de construcción es alto pero el costo de cambio es bajo. Este modo se usa ampliamente en aplicaciones proporcionadas por el código fuente de Android. El ejemplo más típico es la Actividad principal de la aplicación del navegador (llamada Navegador...), que es la ventana que muestra el contenido de la pestaña actual y la página actual. Es costoso de construir, pero el cambio de página es más rápido y coincide muy bien con singleTask.
Por el contrario, singleInstance es más extremo. En la mayoría de los casos, singleInstance y singleTask son iguales. La única diferencia es que la Actividad de singleInstance es la única Actividad en la pila y si toca cualquier otra Actividad, se entregará a otra tarea. Esto hace que la Actividad de instancia única sea como una isla, una caja negra completa a la que no le importa de dónde viene la solicitud ni quién la ejecuta posteriormente.
En las aplicaciones predeterminadas de Android, hay muy pocas actividades de este tipo. En mi práctica de ingeniería personal, he intentado utilizar dichas actividades en la actividad de recogida rápida del Diccionario de Youtube, porque creo que es lo suficientemente conveniente para recoger elementos rápidamente (seleccionando). recoge elementos de la notificación (accesible tocando la entrada) y se utiliza en una variedad de contextos, y debe hacerse de forma completamente independiente.
Además del modo de lanzamiento que se puede usar para proporcionar tareas, también se usa a menudo otro atributo de
Proceso
En la mayoría de las otras plataformas, cada desarrollador tiene una comprensión muy clara del modelo de proceso para su aplicación. Por ejemplo, en un programa de consola, puede imaginar iniciar un proceso desde la función principal y salir al final de la función principal después de que se completa la ejecución del proceso. En un programa de interfaz de usuario, generalmente hay un bucle de mensajes ejecutándose, y cuando; Se recibe el proceso. Mensaje de salida, el bucle de mensajes saldrá para finalizar el proceso. Durante la ejecución del programa, cada desarrollador sabe tan claramente como un espejo qué proceso iniciar, cómo comunicarse con procesos de terceros, etc. Los límites del proceso, al igual que aquí las fronteras nacionales, dejan una profunda huella cada vez que se cruzan.
En los programas de Android, los desarrolladores a menudo solo perciben las tareas directamente.
En los programas de Android, lo que los desarrolladores pueden percibir directamente a menudo son sólo tareas. Lo que está claro es el límite de los componentes, mientras que el límite del proceso se vuelve difícil de alcanzar, e incluso existe el alojamiento del proceso.
Por supuesto, el hecho de que Android oculte los detalles del proceso no es deliberado, sino natural. Por ejemplo, si a las aplicaciones tradicionales las llamamos desarrollo orientado a procesos, en Android tenemos desarrollo orientado a componentes. Del contenido anterior podemos saber que los saltos y la comunicación entre componentes de Android se realizan bajo la premisa de la intervención de terceros. Debido a esta intervención, los dos componentes generalmente no tienen contacto directo (en la comunicación del Servicio, existe). No hay necesidad de intervención de terceros, por lo que Android supone que todos se convierten en cruces de límites de procesos y se comunican según RPC. De esta manera, los detalles del proceso están ocultos). Por lo tanto, no importa si cruzan los límites del proceso. Por lo tanto, si los desarrolladores aún necesitan prestar atención al proceso en este momento, se volverá muy extraño y muy confuso. En pocas palabras, Android elimina todos los procesos y el alojamiento, y la capa superior no necesita conocer la vida, la muerte y la vida. Detalles de comunicación del proceso.
En la parte inferior de Android, el proceso crea un grupo en ejecución en la parte inferior, no solo los diversos componentes de Actividad en la Tarea, sino también los otros tres componentes principales: Servicio, Proveedor de contenido y Receptor de transmisión, todos los cuales son ejecuciones alojadas en algún proceso subyacente. Aquí, el proceso se parece más a un grupo de recursos (el concepto es como un grupo de subprocesos, la capa superior simplemente lo saca cuando quiere usarlo y no presta atención a cuál toma ...).
Los componentes del sistema solo están destinados a albergar cada componente y no se preocupan por la relación lógica directa entre cada componente. Pero podemos imaginar que para garantizar la integridad, Android definitivamente tenderá a incluir la misma tarea y cada componente de la misma aplicación en el mismo proceso de forma predeterminada. Por supuesto, por razones de eficiencia, Android también permite el desarrollo.
En Android, tanto la
Además del atributo
Dado que Android ayuda a los desarrolladores a alojar procesos, requiere un conjunto complejo de algoritmos para realizar la lógica de reciclaje. En Android, la vida y la muerte de un proceso están estrechamente relacionadas con los componentes que se ejecutan en el proceso. El proceso se prioriza de acuerdo con las características de los componentes en los que se ejecuta y se recicla de baja prioridad a alta prioridad. Los procesos de Android se dividen en cinco categorías de prioridad: proceso en primer plano, proceso visible, proceso de servicio, proceso en segundo plano y proceso vacío. Como sugiere el nombre, no es difícil ver que esto significa que el proceso que está más estrechamente relacionado con las operaciones del usuario tiene más interacciones con el usuario. Los procesos que interactúan más con el usuario tienen mayor prioridad y son más difíciles de reciclar. Para obtener más información, consulte: guía/topics/fundamentals.html#proclife.
Una vez que haya establecido prioridades, también deberá acertar en el momento adecuado. Restaurar demasiado pronto hará que la probabilidad de acierto de caché sea baja, lo que puede provocar la creación y destrucción continua de procesos, y las ventajas del grupo se eliminarán. Restaurar demasiado tarde, la sobrecarga general será alta y la eficiencia operativa del sistema será baja; reducido, y un buen Ferrari puede convertirse en un coche clásico QQ. Lo más importante a considerar al restaurar un proceso de Android es la sobrecarga de memoria, la electricidad y otros recursos. Además, los indicadores cuantitativos como la cantidad de componentes que lleva cada proceso, la cantidad de componentes por proceso y la cantidad de procesos abiertos. mediante una sola aplicación también se utilizan marcas importantes para medir. Además, también se controlará estrictamente cierta sobrecarga del tiempo de ejecución y los procesos que se inician lentamente se eliminarán por la fuerza. Android comprueba periódicamente los parámetros anteriores e intenta reciclar el proceso en los puntos en los que podría ocurrir (por ejemplo, después de que un componente completa la ejecución).
Desde la perspectiva de la experiencia del usuario, el mecanismo de proceso de Android tiene un lado muy popular. Algunos programas se inician muy lentamente, pero si sale y reutiliza repetidamente estos programas cuando los recursos son suficientes, la velocidad de inicio será muy rápida (el proceso no muere, simplemente pasa del fondo al primer plano. Esto es gracias). para procesar el hosting.
Por supuesto, la otra cara de la gran alegría es que el algoritmo de alojamiento de Android también muestra su lado infantil de vez en cuando. Es obvio que los usuarios han sentido claramente que la velocidad de funcionamiento del sistema operativo se ha ralentizado cuando abren el administrador de tareas. , pueden ver que muchas aplicaciones todavía están vivas y coleando. Ayudarlos manualmente a terminar con sus vidas y encontrar una tumba hace que el administrador de tareas sea básicamente Android
Esta es la primera vez que veo la tarea. administrador en un dispositivo Android.
Desde una perspectiva de desarrollo, el mecanismo de proceso de Android libera a los desarrolladores. Los desarrolladores no necesitan preocuparse por crear un proceso en segundo plano para monitorear en secreto un determinado momento y tratar de utilizar varios medios para protegerlo. Forjar su propio proceso es como un fénix. Los problemas de vida o muerte y los problemas principales del proceso son. todo gestionado por desarrolladores ordinarios dentro del rango. Pero al mismo tiempo, en la controversia entre GC y la gestión de la memoria humana, todos los desarrolladores no creen que el algoritmo pueda hacerlo de manera más eficiente y mejor que ellos mismos. Pero siempre he creído firmemente que todas las ventajas de eficiencia desaparecerán a medida que mejoren el algoritmo y el hardware, sólo que la simplicidad del modelo de desarrollo no cambiará con el tiempo.
Ciclo de vida del componente
Cualquier cambio arquitectónico conducirá a cambios en el modelo de desarrollo de nivel superior. Aunque el modelo de proceso de Android no requiere que los desarrolladores presten mucha atención al momento en que se crean y destruyen los procesos, aún deben prestar atención al impacto de estos puntos de tiempo en los componentes. Por ejemplo, es posible que necesite conservar el contenido escrito en la memoria y el disco antes de que se destruya el proceso, lo que requiere prestar atención a algunos eventos que ocurren antes de que finalice el proceso.
En Android, comprender estos tiempos requiere comprender el ciclo de vida de los componentes. El llamado ciclo de vida del componente se refiere a los eventos que ocurren cuando un componente cambia entre el front-end y el back-end, es creado por el usuario, sale y el sistema lo recicla. Algunos eventos se notificarán a los componentes correspondientes y los desarrolladores pueden manejar estos eventos de forma selectiva para completar algún trabajo adicional en el momento correspondiente.
Además de los proveedores de contenido, otros componentes también tendrán el concepto de ciclo de vida y todos deben seguir este modelo para manejar algunas condiciones con regularidad; consulte: guía/topics/fundamentals.html#lcycles . Aquí, el primero en atrapar al ladrón, o sacar la Actividad como modelo, es la Actividad.
Continuar robando imágenes del SDK, este es un viaje natural a través del ciclo de vida de la Actividad, comenzando con onCreate y terminando con onDestroy. Sin embargo, las personas tienen sus altibajos, la luna crece y mengua, y los componentes tienen sus bendiciones y sus desgracias. Cuando el sistema los necesita, los componentes se ubican en un segundo plano y los procesos que contienen pueden ser reciclados por el país en cualquier momento. tiempo Esto hace que sea muy importante entender cómo acceder al fondo.
Cuando el componente ingresa a la parte superior de la pila y comienza a interactuar con el usuario, se llamará a la función onResume; de manera similar, cuando el componente sale de la parte superior de la pila, se llamará a la función onPause; Porque, cuando el componente ya no se ejecuta en primer plano, es posible que otros componentes necesiten leer y escribir los mismos archivos y configuraciones. Si el trabajo de actualización realizado por onResume ya no se realiza, pueden aparecer datos sucios (por supuesto, la situación específica). se analizará en detalle, y si es necesario que el archivo no sea leído ni escrito por el encabezado, puede invertir en onCreate para realizar el trabajo de lectura).
Además del problema antes mencionado de ser acosado por otros componentes al pasar a segundo plano, la congelación indefinida también es algo terrible. En Android, hay dos formas comunes de que los componentes mueran. Una es morir de forma natural. Por ejemplo, el elemento de la pila ABC se convierte en AB y el componente C también morirá de forma natural. Este tipo de pelo muerto es tan ligero como una pluma y no requiere cuidados especiales. Pero en otro caso, el sistema lo recicla, es decir, no hay salida después de montañas y ríos, y hay un futuro brillante en otro pueblo.
Pero este tipo de muerte es más difícil de entender para los usuarios. Imagínate, un juego no se puede guardar en el disco. Sigues jugando y jugando sin dormir durante tres días y tres noches. En ese momento, tu hermana te llama para animarte. Estás a punto de refrescarte, pero descubres que tu juego. El proceso pasa a un segundo plano. Después de reciclar el sistema, volví a antes de la liberación durante la noche y tres días de arduo trabajo fueron en vano. ¿No podrías matar a la gente en el juego? ¿No serías capaz de hacerlo? ¿No serías capaz de hacerlo?
En este momento, si no existe el ciclo de vida de la Actividad, el programador del juego debe haber sido asesinado por error y convertirse en el chivo expiatorio de Android. Sin embargo, los componentes de Android tienen un ciclo de vida. Si algo así realmente sucede, no dude en matar al programador que lo desarrolló.
Para escapar, los programadores tienen una medalla de oro para evitar la muerte, y ese es el mecanismo de estado de Android. El llamado estado es una forma para que los desarrolladores almacenen parte de la información sobre el estado de ejecución actual en un objeto Bundle. Un objeto Bundle es una colección de pares clave-valor serializables. Si el proceso en el que se encuentra el componente Actividad necesita reciclarse, el kernel de Android conservará el objeto Paquete del componente Actividad en el disco. Cuando el usuario regrese a la Actividad, el sistema reconstruirá el componente y lo restaurará. El paquete persistió en el objeto del disco. Con esta información de estado persistente, los desarrolladores pueden diferenciar entre muertes específicas y tener la oportunidad de restaurar la actividad muerta a su estado previo a la muerte. Lo que los desarrolladores deben hacer es mantener la información de estado a través de la función onSaveInstanceState (en el estado predeterminado, los controles del sistema guardarán su propia información de estado, como TextView, que guardará la información de texto actual. Los desarrolladores no necesitan preocuparse por esto. ... ..). La información de estado se escribe en el objeto Bundle y luego se lee y se restaura en las funciones onRestoreInstanceState (onCreate, onStart, etc.). .
Subprocesos
Los subprocesos, naturalmente, no participan en la lectura de datos ni en el procesamiento en segundo plano. En el nivel de programación del kernel de Android, no se consideran los subprocesos. Solo se centra en los procesos. La construcción y el procesamiento de cada componente se completan en el subproceso principal del proceso, lo que garantiza que la lógica sea lo suficientemente simple. El multiproceso es a menudo todo lo que un desarrollador necesita hacer.
Los subprocesos en Android también se implementan derivando el objeto de subproceso de Java e implementando el método Run. Pero cuando los usuarios necesitan ejecutar subprocesos a través de bucles de mensajes, Handler y Looper de Android brindan un mejor soporte. El trabajo de un Looper, por otro lado, es construir un bucle que espera la salida o la llegada de otros mensajes. En la página SDK de Looper, hay un ejemplo estándar de implementación de un subproceso de bucle de mensajes, pero, por supuesto, un enfoque más estándar podría ser crear un subproceso HandlerThread y pasar su Looper al controlador.
En Android, el uso de Content Provider suele estar relacionado con hilos. Como se mencionó anteriormente, para mantener una mayor flexibilidad, el proveedor de contenido en sí solo proporciona una interfaz para llamadas sincrónicas. Dado que la adición, eliminación y modificación asincrónica del proveedor de contenido es una operación común, Android proporciona llamadas asincrónicas a través de la interfaz del objeto AsyncQueryHandler. . Esta es una subclase de Handler. Los desarrolladores pueden llamar al método startXXX para iniciar la operación y esperar la devolución de llamada después de que se complete la ejecución a través del método onXXXComplete derivado para completar todo el proceso de llamada asincrónica, que es muy simple y claro.
Implementación
La implementación principal de toda la gestión de tareas y procesos se encuentra en ActivityManagerService. Como se mencionó en el artículo anterior, la resolución de Intent es responsabilidad de ActivityManagerService. De hecho, es muy difícil nombrar una clase, porque aunque se llama ActivityManager Service, el alcance que administra no solo incluye Activity, sino también los otros tres. tipos de componentes y los procesos en los que existen.
En ActivityManagerService, hay dos tipos de estructuras de datos que son las más llamativas, una es ArrayList y la otra es HashMap.
Hay una gran cantidad de ArrayLists en ActivityManagerService. En cada componente, habrá múltiples subestados de ArrayList para almacenar el trabajo de programación. A menudo, sale de un ArrayList, encuentra un método para ajustar y luego lo arroja a otro ArrayList. actúa como este componente cuando no corresponde a ArrayList, no está lejos del HashMap muerto, porque hay un componente que debe ubicarse usando el nombre o la información de intención, como el proveedor de contenido. en Uri, todo encaja.
ActivityManagerService utiliza una estructura de datos llamada xxxRecord para representar cada componente vivo. Eso es lo que es el historial (guarda información sobre las actividades, se llama historial porque es relativo a la pila de tareas...).
Vale la pena señalar que los conceptos de TaskRecord y pila de tareas de los que hemos estado hablando, de hecho, la capa inferior real no mantiene una pila de actividades en TaskRecord. En ActivityManagerService, cada actividad de tarea se almacena centralmente en una ArrayList en forma de HistoryRecord. Cada HistoryRecord almacena su referencia en TaskRecord. Cuando una actividad completa la ejecución y sale de la pila de tareas conceptual, Android escanea el HistoryRecord del mismo TaskRecord hacia adelante desde la posición actual del HistoryRecord. Este diseño hace que muchos sistemas de tareas aparentemente complejos en la capa superior sean muy unificados y de implementación simple, lo cual es encomiable.
ProcessRecord es el núcleo de toda la implementación del alojamiento de procesos. Almacena información sobre los procesos y todos los componentes que se ejecutan en el proceso. En base a esta información, el sistema tiene un conjunto de algoritmos para decidir cómo deshacerse de ellos. el proceso Si está interesado en el algoritmo de reciclaje, puede comenzar con la función trimApplications de ActivityManagerService. Si está interesado en el algoritmo de reciclaje, puede comenzar con la función trimApplications de ActivityManagerService.
Para los desarrolladores, comprender la implementación de esta parte es principalmente para ayudar a comprender el concepto de todo el proceso y las tareas. Si cree que comprende esta parte claramente, no necesita tocar el gigante ActivityManagerService. ya no. .
================
Esta es una reimpresión, se siente mejor que el primer piso
= == ==========
Uno propio
El servicio debe configurarse antes de poder usarse
Yo ganaré. No menciono el uso de subprocesos.
Hanler es algo que se usa para colas de mensajes. El controlador se puede usar para actualizar la visualización de controles y comunicarse entre subprocesos.
El servicio solo se puede iniciar en. el fondo y pertenece a los componentes de la aplicación uno