Jetpack Compose vs el sistema de Views: ¿qué tradeoffs debe saber articular un ingeniero senior?
Todo el mundo coincide en que Compose es el futuro — la señal en la entrevista es si sabes nombrar los tradeoffs concretos de build, interoperabilidad y rendimiento en lugar de repetir una diapositiva de marketing.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Jetpack Compose vs el sistema de Views: ¿qué tradeoffs debe saber articular un ingeniero senior?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#La pregunta en realidad es "¿sabes cuándo usar cuál?"
Pedirle a un candidato senior que compare Jetpack Compose y el sistema de Views no trata realmente de si Compose es "mejor" — todo el mundo está de acuerdo en que es la dirección futura de Google. Se trata de si eres capaz de nombrar tradeoffs concretos: tiempo de build, la curva de aprendizaje del equipo, la interoperabilidad, y las clases específicas de bugs que cada modelo facilita o dificulta introducir. Un entrevistador quiere escucharte razonar sobre una decisión de migración real, no repetir una diapositiva de marketing.
#Imperativo vs. declarativo es la verdadera línea divisoria
El sistema de Views es imperativo: construyes un árbol de View una vez (normalmente desde XML), y a partir de ahí lo mutas — textView.text = nuevoValor, progressBar.visibility = View.GONE. Cada mutación es una orden que emites a mano, y depende de ti mantener sincronizado con los datos actuales cada camino que toca un widget. Compose es declarativo: una función @Composable describe cómo debería verse la UI para el estado actual, y el runtime — el compilador de Compose más un recomposer — averigua qué cambió y actualiza solo los nodos necesarios. Nunca mutas un widget directamente; cambias el estado, y la UI que lee ese estado se recompone.
#La recomposición reemplaza el baile de invalidate/measure/layout/draw — pero no es gratis
En el sistema de Views, "redibujar" es invalidate → measure → layout → draw, y puedes disparar las partes costosas por accidente (llamando a requestLayout() dentro de un listener de scroll, por ejemplo). En Compose, el riesgo equivalente es la recomposición innecesaria: un composable se vuelve a ejecutar más a menudo de lo necesario porque sus entradas no son estables (una lambda capturada de nuevo en cada recomposición, una List en vez de una ImmutableList, una data class que el compilador no puede demostrar que es estable). El modelo mental pasó de "¿invalidé con demasiada avidez?" a "¿le di al compilador suficiente información para saltarse este composable?" — un bug distinto, pero de la misma categoría de trampa de rendimiento, y es exactamente el tipo de matiz que distingue a alguien que ha llevado Compose a producción de alguien que solo siguió un tutorial.
#Dónde ocurre realmente la interoperabilidad
Nadie migra una app grande de una sola vez, y por eso existen ComposeView (insertar un subárbol de Compose dentro de un layout XML) y AndroidView (insertar una View heredada — un MapView, un widget de un SDK de terceros que todavía no tiene wrapper de Compose — dentro de un árbol de Compose) como las dos primitivas de interoperabilidad. Una respuesta senior pragmática nombra directamente la estrategia de "higuera estranguladora" (strangler fig): pantallas nuevas en Compose, pantallas existentes migradas de forma oportunista cuando de todos modos hay que tocarlas, y AndroidView/ComposeView como la costura que permite que ambos convivan durante años en lugar de bloquear el trabajo esperando una reescritura completa.
#Comparativa lado a lado
| Aspecto | Sistema de Views | Jetpack Compose |
|---|---|---|
| Modelo de programación | Imperativo, mutas el árbol | Declarativo, describes el estado |
| Sincronización de estado | Manual (mantienes UI y datos sincronizados tú mismo) | Automática (la recomposición reacciona al estado) |
| Preview/iteración | Layout Editor, ciclos lentos de inflado de XML en pantallas con mucha lógica | @Preview, casi instantáneo para iterar solo sobre UI |
| Animación | Verbosa (ObjectAnimator, MotionLayout) | APIs de animación integradas y componibles |
| Testing | Espresso, view matchers | APIs de test de Compose, árbol de semántica |
| Soporte de librerías/SDKs | Universal — todo SDK incluye una View | Mejorando, pero no universal; algunos SDKs siguen siendo solo-View |
| Bug de rendimiento típico | Disparar pasadas de layout de más | Entradas inestables que provocan recomposición excesiva |
// Sistema de Views: layout XML (res/layout/activity_counter.xml) + Activity// <TextView android:id="@+id/countText" .../>// <Button android:id="@+id/incrementButton" .../>class CounterActivity : AppCompatActivity() {private var count = 0private lateinit var binding: ActivityCounterBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityCounterBinding.inflate(layoutInflater)setContentView(binding.root)binding.incrementButton.setOnClickListener {count++binding.countText.text = "Count: $count"}}}
// Compose: sin XML, sin sincronización manual — el estado guía la UI@Composablefun CounterScreen() {var count by remember { mutableStateOf(0) }Column {Text(text = "Count: $count")Button(onClick = { count++ }) {Text("Increment")}}}