Developing Frogtek

El blog del Departamento de Tecnología

El findViewById(), y lo cargante que puede llegar a ser

Si no vienes del post introductorio, quizás te gustaría leerlo.

Segundo asalto. La aplicación sigue estando lenta, y no sabemos muy bien por qué. Ya no hay tantos onMeasure(); sólo los “necesarios”. Pero hay una nueva función que aparece en los primeros puestos de nuestro querido traceview: findViewTraversal(). ¿Y quién llama tantísimas veces a esta última? pues findViewById().
Y es que esta función, es la que enlaza un recurso de la interfaz de usuario de una aplicación, con una variable en nuestro código. O simplemente se encarga de acceder a ese recurso para cambiar una de sus propiedades.

Supongamos, pues, que tenemos un layout con una veintena de widgets entre layouts, Buttons, EditText, TextView, ImageView… Y que en un momento dado del ciclo de vida de la actividad, tenemos que acceder a uno ó varios de ellos para cambiar sus propiedades. Podemos acceder a ellos del modo:
((EditText) this.findViewById(R.id.edittext_cliente)).setOnClickListener(…);
Aunque también podemos guardar el valor que devuelve la función findViewById() en una variable, o variable miembro de la Activity para operar con ella varias veces a lo largo del citado ciclo de vida.

¿Cuál es la diferencia? y ya que estamos ¿cuál era nuestro error? Pues que cada vez que se llama a la función findViewById(), android recorre todo el árbol jerárquico del Layout que hemos definido previamente en XML, en busca del recurso al que queremos acceder. Como ya habréis averiguado, el costo de esa operación es muy alto si el control a encontrar es de los últimos en el árbol, o si hay muchos niveles de layouts anidados, o simplemente queremos cambiar el valor de una de las propiedades de ese recurso bastantes veces a lo largo del ciclo de vida de la aplicación.
Si a esto le sumamos que el cambio en una de las propiedades de un EditText (por ejemplo), afecta a cinco o seis widgets más; y a todos accedemos del modo findViewById(), estamos vendidos.

La solución para una Activity, cuyos widgets han de ser modificados con bastante asiduidad es el guardar el resultado de la función archinombrada en este post en variables miembro, a las cuales se accederá sin tener que recorrer (findViewTraversal()) todo el árbol jerárquico “n” veces, con la consiguiente pérdida de velocidad en la transición de un estado a otro de la Activity.
Eso sí: Podemos prescindir de estas variables miembro si accedemos una, y sólo una, vez al widget en cuestión.

Esto también hay que grabarlo a fuego en la mente.

3 Comentarios

  1. Muy buen artículo.

    Yo añadiría la importancia de implementar estas optimizaciones en los métodos getView() de las clases Adapter ya que se nota mucho en su eficiencia.

    Una buena idea es usar una clase contenedora que se almacena en el tag del objeto View. Así no tenemos que hacer el findViewById cada vez que el usuario mueve el scroll:


    public class PlayerListAdapter extends ArrayAdapter {

    /*
    .
    . resto de clase
    .
    */

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    View row = convertView;
    ViewHolder holder;

    if (row == null) {
    row = mInflater.inflate(R.layout.team_info_player_item, null);
    holder = new ViewHolder(row);

    row.setTag(holder);
    } else {
    holder = (ViewHolder) row.getTag();
    }

    Player player = getItem(position);

    holder.dorsal.setText(""+player.getDorsal());
    holder.name.setText(""+player.getName());
    holder.position.setText(""+player.getPosition());

    return row;
    }

    private class ViewHolder {
    public TextView dorsal;
    public TextView name;
    public TextView position;

    public ViewHolder(View row) {
    dorsal = (TextView) row.findViewById(R.id.Dorsal);
    name = (TextView) row.findViewById(R.id.Name);
    position = (TextView) row.findViewById(R.id.Position);
    }

    }

    }

  2. Gracias por tu aportación Francho. Ya has desvelado parte del siguiente post 🙂

    Aquí al lado hay unos chicos que creen verte todas las mañanas, jejejeje.

  3. Ooops… pues ya siento haber metido la pata (borra/edita el comentario para mantener la intriga), si es que uno es de un bocas. 😉

    Lo de las “apariciones matutinas” fácil que sea yo, porque creo recordar que en vídeo de “un día en Frogtek” salían calles que yo recorro semidormido.

    Yo soy muy malo para recordar caras, así que no creo que los reconozca (a ver si repaso el vídeo) de todas formas, que me saluden y así nos “desvirtualizamos” 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada.

*