3

I have a ListView in my app, and I've over-ridden the getView() method so I can change the row's ImageView src depending on the row's text.

The problem is, I've noticed the ListView scrolling is lagging, and when I check DDMS, it seems the Garbage Collector is being called everytime the ListView is being scrolled, thus slowing the scrolling.

I've also noticed the Garbage Collector being called in a different part of my app, when reading lines from a BufferedReader, which makes opening a 2,000 line file take ~47 seconds, where as a file exporer I have installed on my phone opens that same file in about 2 seconds.

So my question is, what could be causing the constant Garbage Collection every 200ms or so, and how do I prevent it? It's really slowing my app down and I fear it will put some users off if I don't solve it.

Thanks, Alex.

ListView getView():

class IconicAdapter extends ArrayAdapter<String> {
  IconicAdapter(){
    super(FileBrowser.this, R.layout.filebrowser_listview_row, R.id.listtext, directoryEntries);
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent){
    View row = super.getView(position, convertView, parent);
    TextView text = (TextView) row.findViewById(R.id.listtext);
    ImageView icon = (ImageView) row.findViewById(R.id.listicon);

    entryFullFileName = directoryEntries.get(position).toString();

    if(entryFullFileName.contains(".") && !entryFullFileName.matches("^\\.+$")){
      String[] parts = entryFullFileName.split("\\.");
      lastIndex = parts.length - 1;
      fileType = parts[lastIndex];
    }else{
      fileType = "";
    }

     if(fileIsDir.get(position) == true){
      icon.setImageResource(R.drawable.folderlightblue);
     }else if(fileType.equals("html")){
      icon.setImageResource(R.drawable.filehtml);
     }else if(fileType.equals("css")){
      icon.setImageResource(R.drawable.filecss);
     }else if(fileType.equals("js")){
      icon.setImageResource(R.drawable.filejs);
     }else if(fileIsDir.get(position) == false){
      icon.setImageResource(R.drawable.fileplain);
     }

   return(row);
  }
}  

Code To Open File

I removed the code the other day that logged how many seconds it took to open the file, but it took 47 seconds and definitely took too long, and again while the while loop is doing it's thing, there's constant calls to the Garbage Collector, which I'm guessing in the cause of the slow file reading - and yes, this function is called in a thread with progressDialog showing while the file is being read

private String getLocalFileContents(String fileUri){
  try{
    String contents = "";
    BufferedReader in = new BufferedReader(new FileReader(fileUri));
    String line;
    while((line = in.readLine()) != null){
      contents += line + "\n";
    }
    in.close();

    return contents;
  }catch(Exception e){
    toast.setText("Failed to open sdcard file, try again.");
  }
 return null;
}

UPDATE:

The file reading problem is solved, turns out the String concatenation made the Garbage Collector get called after each loop, dramatically slowing the file reading down. As suggested by an answer I used StringBuilder instead and it now opens in a second - hooray!

2ND UPDATE:

I know what the cause of the constant GC calls when scrolling my ListView is, it's the ListView attribute android:cacheColorHint="@android:color/transparent" - but I don't know a work-around!

AlexPriceAP
  • 1,987
  • 6
  • 26
  • 41
  • 1
    I think you really need to post the relevant parts of your code - it's pretty impossible to tell what's going on without seeing it. – Squonk Aug 23 '11 at 19:32
  • 1
    One possible thing with your file reading problem is `contents += line + "\n";` - your `contents` variable is a `String` and because strings are immutable, every time you do a concatenation using `+=` a new `String` has to be created and the old `String` is then left for GC. Try using a `StringBuilder` with `append(String str)` instead. – Squonk Aug 23 '11 at 20:02
  • When/Where does getFileContents ever get called? Does it actually have anything to do with the listview? – Sashi Kolli Aug 23 '11 at 20:04
  • [similar question for reading from a file](http://stackoverflow.com/questions/326390/how-to-create-a-java-string-from-the-contents-of-a-file) – Sashi Kolli Aug 23 '11 at 20:06
  • MisterSquonk, I actually love you! (In a none-gay way). Using a StringBuilder instead has allowed the 2,000 line file to be read in a second. Thank you so much! I'll have to remember to use a StringBuilder instead of concatenation in the future! – AlexPriceAP Aug 23 '11 at 20:08
  • @Sashi - no the ListView is for my file browser, it doesn't relate to the file reading (which is now solved), it's just that both the ListView and the file reading script were slowed down due to the Garbage Collector constantly being called. – AlexPriceAP Aug 23 '11 at 20:12
  • workaround: do not use the cacheColorHint :D – Danail Jan 11 '13 at 09:15

4 Answers4

1

In general, garbage collection is happening because you're creating too many objects unnecessarily. It'd be easier to help with your code, but I'll give it a shot anyway.

In the case of your list, you're probably recreating your view in every call to getView. You should instead re-use convertView when appropriate. See my answer to this other SO question for an idea of how to structure your getView method.

Your file reading problem is a bit harder to guess at, but 47s seems ridiculously long for 2,000 lines. Are you also creating objects in that loop?

Update:

So apparently your problem isn't really with your View objects themselves, but it's all the work you do every time you get a View. You're doing quite a bit of work every time: a RegEx match, string splitting (and associated string object creation), etc. You should at minimum cache the results of this so that you don't have to redo the work for each item every time it comes back into view.

Community
  • 1
  • 1
kabuko
  • 36,028
  • 10
  • 80
  • 93
  • Good point, still, I would've thought a simple regex would be quite quick. I'll try caching the result. Thanks for the help :) – AlexPriceAP Aug 23 '11 at 19:52
  • Hmm, I just commented everything out in getView() except View row = super.getView(position, convertView, parent), and it was still slow with the GC keep being called :S – AlexPriceAP Aug 23 '11 at 19:59
  • It's fine, I found the problem now, it's android:cacheColorHint that slowing everything down. I have a gradient as my activity's background so if I remove android:cacheColorHint it makes the activity look horrible! :( – AlexPriceAP Aug 23 '11 at 20:39
1

One optimization would be to stop splitting the entire string to get the filetype. You could use something like

String fileType = "";
int lastDot = entryFullFileName.lastIndexOf(".");
if(lastDot!=-1) {
    fileType = entryFullFileName.substring()
}

That certainly shouldn't take 47s though.

Sashi Kolli
  • 751
  • 5
  • 6
  • I just tried commenting out everything but View row = super.getView(position, convertView, parent) and it was still lagging the same :S :( – AlexPriceAP Aug 23 '11 at 20:01
0

Yes, android:cacheColorHint="@android:color/transparent" is causing excessive calling of the garbage collector on some OS versions (not sure if the newest ones this is fixed).

Well, just try to not use it. For example, I spoke with my designers, explained them the problem about the cause of the lags, and they agreed to not use the transparent background.

Danail
  • 10,443
  • 12
  • 54
  • 76
0

Look at EfficientAdapter here is link

And explained more about efficient adapter and getView method in other thread have a look at it, here is link

Hope this help!!!

Community
  • 1
  • 1
Gopal
  • 1,719
  • 12
  • 13