Aller au contenu

ListView / performance / setText / Spannable


Pierre87

Recommended Posts

Salut !

Dans mon appli, j'utilise de nombreuses ListView avec des vues customs.

Je connais déjà le principe du recyclage et des view holders (et je l'applique)

Les performances sont bonnes, mais pas exceptionnelles :(

J'ai utilisé le "method profiling" et j'ai constaté un petit problème de performance :

Lorsque je réutilise une View de ma ListView, je la mets à jour avec mes données.

C'est cette fonction qui prend un peu de temps, et fait saccader ma ListView

La vue contient entre autre une TextView.

Or, grâce au "method profiling", j'ai vu que le setText() prenait énormément de temps par rapport aux autres fonctions! (55% du temps de la mise à jour de ma vue)

Ce setText est particulier, puisque je mets dans ma TextView un Spannable.

Vous avez des idées?

Lien vers le commentaire
Partager sur d’autres sites

ouais c'est vrai...

Quand je regarde les logs Android, je vois que le GC s'active un peu de temps à autre, mais je "sens" que ce n'est pas ça qui provoque les saccades (pas au mêmes moments)

J'ai "plus de saccades que de GC"

Comme c'est pour ma boite, le code n'est pas vraiment open source, mais bon...

   public void update(JSONObject jsonStory)
   {
       this.json = jsonStory;

       this.spannableText.clear();

       JSONObject jsonUser = this.json.optJSONObject("user");

       if (jsonUser != null)
       {
           this.spannableUser.update(jsonUser);

           this.spannableText.append(this.spannableUser);
       }

       JSONObject jsonPlace = this.json.optJSONObject("place");

       if (jsonPlace != null)
       {
           if (this.spannableText.length() != 0)
           {
               this.spannableText.append(" ");
           }

           this.spannableText.append(jsonStory.optBoolean("isCheckin", true) ? "@ " : "› ");

           this.spannablePlace.update(jsonPlace);

           this.spannableText.append(this.spannablePlace);
       }

       String storyContent = this.json.optString("content", null);

       if (storyContent != null && storyContent.length() > 0)
       {
           this.spannableText.append(" : " + storyContent);
       }

       Linkify.addLinks(this.spannableText, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES);

       this.text.setText(this.spannableText);

       String textTimeUsers = this.json.optString("parsed_date", "");
       int userCount = jsonStory.optInt("userCount", 0);

       if (userCount > 0)
       {
           if (userCount == 1)
           {
               textTimeUsers += this.context.getString(R.string.and_one_other);
           }
           else
           {
               textTimeUsers += this.context.getString(R.string.and_n_others, userCount);
           }
       }

       this.timeUsers.setText(textTimeUsers);

       int storyCommentCount = this.json.optInt("commentCount", 0);

       if (storyCommentCount == 0)
       {
           this.commentCount.setText(null);
           this.commentCount.setBackgroundResource(R.drawable.story_comment_count_0);
       }
       else
       {
           this.commentCount.setText(Integer.toString(storyCommentCount));
           this.commentCount.setBackgroundResource(R.drawable.story_comment_count_n);
       }

       if (jsonUser != null)
       {
           this.icon.setImageResource(R.drawable.user_default_icon);
           this.icon.setImageUrl(jsonUser.optString("icon", null));
       }
       else if (jsonPlace != null)
       {
           this.icon.setImageResource(R.drawable.place_default_icon);

           JSONObject category = jsonPlace.optJSONObject("category");

           if (category != null)
           {
               this.icon.setImageUrl(category.optString("icon", null));
           }
       }
       else
       {
           this.icon.setImageBitmap(null);
       }

       String pictureUrl = null;
       int pictureWidth = 0;
       int pictureHeight = 0;

       JSONObject jsonPic = this.json.optJSONObject("pic");

       if (jsonPic != null)
       {
           JSONObject pic150x150 = jsonPic.optJSONObject("150x150");

           if (pic150x150 != null)
           {
               float density = this.context.getResources().getDisplayMetrics().density;

               pictureWidth = (int) (pic150x150.optInt("width", 0) * density);
               pictureHeight = (int) (pic150x150.optInt("height", 0) * density);

               pictureUrl = pic150x150.optString("url", "");
           }
       }

       this.picture.setImageBitmap(null);
       this.picture.setImageUrl(pictureUrl, pictureWidth, pictureHeight);

       int friendsOnlyVisibility = jsonStory.optBoolean("isFriendsOnly", false) ? View.VISIBLE : View.GONE;
       this.friendsOnly.setVisibility(friendsOnlyVisibility);
   }

Il s'agit d'une méthode DANS mon view holder

Elle est appelée dans le getView de mon Adapter, pour mettre à jour la vue.

Les variables de mon view holder sont :

   private View view;

   private WebImageView icon;
   private TextView text;
   private WebImageView picture;
   private TextView timeUsers;
   private ImageView friendsOnly;
   private TextView commentCount;

   private JSONObject json;

   private SpannableStringBuilder spannableText;
   private User.Spannable spannableUser;
   private Place.Spannable spannablePlace;

Si j'ai bien interprété le "methode tracing", c'est l'appel :

this.text.setText(this.spannableText);

qui me prend 55% de ma méthode update()

J'ai essayé d'instancier le moins possible d'objets dans cette méthode...

Mais je suis bloqué par le setText() :(

Ce view holder est lié à une View en xml.

Celle ci a une complexité de 3 niveaux de profondeur. (en incluant la racine, ce qui est assez peu je pense)

Lien vers le commentaire
Partager sur d’autres sites

Pour le setText(), j'ai regardé un peu l'implem dans TextView, je ne vois pas trop comment on pourrait gagner dedans (à part en réduisant autant que possible le nombre de spans).

Par contre dans ce qu'il y a autour, je suis étonné qu'à chaque getView() tu recrées ton texte à partir du JSON. Il serait intéressant de conserver dans un cache le résultat de ta conversion JSON => Spannable pour éviter d'avoir à le recréer à chaque fois

Attention également à l'affectation des images... mais je suppose que la classe WebImageView est sensée optimiser cet aspect.

Lien vers le commentaire
Partager sur d’autres sites

L'initialisation du Spannable ne coûte quasiment rien en temps :/

Et ça m'obligerai à les stocker quelque part (donc encore plus de mémoire occupée, GC, etc...)

J'ai joint une capture du "method tracing"

Je ne sais pas trop comment l'interpréter (surtout le 8,5% en haut)

setImageUrl est asynchrone

et j'utilise les Bitmap à la taille maximum (pas besoin de redimensionner)

Si vous voulez tester, c'est l'application "Plyce" sur le Market

Lien vers le commentaire
Partager sur d’autres sites

  • 2 weeks later...

               android:layout_width="0dip"
               android:layout_height="wrap_content"
               android:orientation="vertical"
               android:layout_weight="1"
               android:background="@drawable/settings_form_background">
                   android:id="@+id/firstname"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:layout_margin="10dip"
                   android:singleLine="true"
                   android:hint="@string/firstname"
                   android:background="#ffffff" />
                   android:layout_width="fill_parent"
                   android:layout_height="1dip"
                   android:background="#ffd1af" />
                   android:id="@+id/lastname"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:layout_margin="10dip"
                   android:singleLine="true"
                   android:background="#ffffff"
                   android:hint="@string/lastname" />

   xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
       android:color="#ffffff" />
       android:radius="10dip" />
       android:width="1dip"
       android:color="#ffd1af" />

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...