I’ve been messing around with Android-based code for a few months now while hacking onNative Firefox for AndroidandPattrn. I noticed that the performance tips forListViewsare a bit scattered in different sources. This post is an attempt to summarize the ones I found most useful.

I’m assuming you’re already familiar withListViewsand understand the framework aroundAdapterViews. I’ve added some Android source code pointers for the curious readers willing to understand things a bit deeper.

How it works

ListViewis designed for scalability and performance. In practice, this essentially means:

  1. It tries to do as few view inflations as possible.
  2. It only paints and lays out children that are (or are about to become) visible on screencode.

The reason for 1 is simple: layout inflations are expensive operationscode. Although layout files are compiled into binary form for more efficient parsingcode, inflations still involve going through a tree of special XML blockscodeand instantiating all respective views.ListViewsolves this problem by recyclingcodenon-visible views—called “ScrapViews” in Android’s source code—as you pan around. This means that developers can simply update the contents of recycled viewscodeinstead of inflating the layout of every single row—more on that later.

In order to implement 2,ListViewuses the view recycler to keep adding recycled views below or above the current viewport and moving active views to a recyclable pool as they move off-screencodewhile scrolling. This wayListViewonly needs to keep enough views in memory to fill its allocated space in the layout and some additional recyclable views—even when your adapter has hundreds of items. It will fill the space with rows in different ways—from top, from bottom, etc—depending on how the viewport changedcode. The image below visually summarizes what happens when you pan aListViewdown.

With this framework in mind, let’s move on to the tips. As you’ve seen above,ListViewdynamically inflates and recycles tons of views when scrolling so it’s key to make your adapter’sgetView()as lightweight as possible. All tips resolve around makinggetView()faster in one way or another.

View recycling

Every timeListViewneeds to show a new row on screen, it will call thegetView()method from its adapter. As you know,getView()takes three arguments arguments: the row position, aconvertView, and the parentViewGroup.

TheconvertViewargument is essentially a “ScrapView” as described earlier. It will have a non-null value whenListViewis asking you recycle the row layout. So, whenconvertViewis not null, you should simply update its contents instead of inflating a new row layout. ThegetView()code in your adapter would look a bit like:

public View getView(int position, View convertView, ViewGroup parent) {    if (convertView == null) {        convertView = mInflater.inflate(R.layout.your_layout, null);    }    TextView text = (TextView) convertView.findViewById(R.id.text);    text.setText("Position " + position);    return convertView;}

View Holder pattern

Finding an inner view inside an inflated layout is among the most common operations in Android. This is usually done through aViewmethod calledfindViewById(). This method will recursively go through the view tree looking for a child with a given IDcode. UsingfindViewById()on static UI layouts is totally fine but, as you’ve seen,ListViewcalls the adapter’sgetView()very frequently when scrolling.findViewById()might perceivably hit scrolling performance inListViews—especially if your row layout is non-trivial.

The View Holder pattern is about reducing the number offindViewById()calls in the adapter’sgetView(). In practice, the View Holder is a lightweight inner class that holds direct references to all inner views from a row. You store it as atagin the row’s view after inflating it. This way you’ll only have to usefindViewById()when you first create the layout. Here’s the previous code sample with View Holder pattern applied:

public View getView(int position, View convertView, ViewGroup parent) {    ViewHolder holder;    if (convertView == null) {        convertView = mInflater.inflate(R.layout.your_layout, null);        holder = new ViewHolder();        holder.text = (TextView) convertView.findViewById(R.id.text);        convertView.setTag(holder);    } else {        holder = convertView.getTag();    }    holder.text.setText("Position " + position);    return convertView;}private static class ViewHolder {    public TextView text;}

Async loading

Very often Android apps show richer content in eachListViewrow such as images. Usingdrawable resourcesin your adapter’sgetView()is usually fine as Android caches those internallycode. But you might want to show more dynamic content—coming from local disk or internet—such as thumbnails, profile pictures, etc. In that case, you probably don’t want to load them directly in your adapter’sgetView()because, well, you shouldnever ever block UI thread with IO. Doing so means that scrolling yourListViewwould look anything but smooth.

What you want to do is running all per-row IO or any heavy CPU-bound routine asynchronously in a separate thread. The trick here is to do that and still comply withListView‘s recycling behaviour. For instance, if you run anAsyncTaskto load a profile picture in the adapter’sgetView(), the view you’re loading the image for might be recycled for another position before theAsyncTaskfinishes. So, you need a mechanism to know if the view hasn’t been recycled once you’re done with the async operation.

One simple way to achieve this is to attach some piece of information to the view that identifies which row is associated with it. Then you can check if the target row for the view is still the same when the async operation finishes. There are many ways of achieving this. Here is just a simplistic sketch of one way you could do it:

public View getView(int position, View convertView,        ViewGroup parent) {    ViewHolder holder;    ...    holder.position = position;    new ThumbnailTask(position, holder)            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);    return convertView;}private static class ThumbnailTask extends AsyncTask {    private int mPosition;    private ViewHolder mHolder;    public ThumbnailTask(int position, ViewHolder holder) {        mPosition = position;        mHolder = holder;    }    @Override    protected Cursor doInBackground(Void... arg0) {        // Download bitmap here    }    @Override    protected void onPostExecute(Bitmap bitmap) {        if (mHolder.position == mPosition) {            mHolder.thumbnail.setImageBitmap(bitmap);        }    }}private static class ViewHolder {    public ImageView thumbnail;    public int position;}

Interaction awareness

Asynchronously loading heavier assets for each row is an important step to towards a performantListView. But if you blindly start an asynchronous operation on everygetView()call while scrolling, you’d be wasting a lot of resources as most of the results would be discarded due to rows being recycled very often.

We need to add interaction awareness to yourListViewadapter so that it doesn’t trigger any asynchronous operation per row after, say, a fling gesture on theListView—which means that the scrolling is so fast that it doesn’t make sense to even start any asynchronous operation. Once scrolling stops, or is about to stop, is when you want to start actually showing the heavy content for each row.

I won’t post a code sample for this—as it involves too much code to post here—but the classicShelves appby Romain Guy has a prettygood example. It basically triggers the async book cover loading once theGridViewstops scrolling among other things. You can also balance interaction awareness with an in-memory cache so that you show cached content even while scrolling. You got the idea.

That’s all!

I strongly recommend watching Romain Guy and Adam Powell’stalkaboutListViewas it covers a lot of the stuff I wrote about here. Have a look atPattrnif you want to see these tips in action. There’s nothing new about the tips in this post but I thought it would be useful to document them all in one place. Hopefully, it will be a useful reference for hackers getting started on Android development.

Quick update.I’ve just announcedSmoothie, a tiny library that that offers an easy way to do async loading inListViews/GridViews. It incorporates most of the tips described in this blog post behind a very simple API. Have a look!

Got something to add? Find me onTwitterandGoogle+

UI engineer passionate about design. Currently at Mozilla hacking on Firefox for Android. Formerly at litl and Nokia. Music lover, father, husband.More about me→





Reference From :http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/

Also Reference From:http://www.eoeandroid.com/thread-272929-1-1.html


更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. 详解php命令行写shell实例
  2. 分享4个提高脚本性能的PHP技巧
  3. 分析php生成短网址/短链接原理和用法实例
  4. 详解VSCode+PHPstudy配置PHP开发环境的步
  5. php array_unshift()函数详解(实例)
  6. 详解PHP论坛实现系统的思路
  7. 值得一看!高级PHP工程师必备的编码技巧及
  8. 实例讲解php提交表单关闭layer弹窗iframe
  9. 控制反转原则,它和依赖注入有什么联系
  10. 教你使用mixphp打造多进程异步邮件发送