Reputation: 36068
I am working on an Andorid appliation which will list a lot of books by a ListView
, in fact I've used ListView
for a while, I know the basic usage of it.
And I also read this paper: http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
However I found that even I follow the tips mentioned in the article for example use the "View recycling" and "Async loading", scrolling the ListView looks no-smooth.
This the adapter:
class BookAdapter extends ArrayAdapter<Book> {
private List<Book> data;
public BookAdapter(Context context, int resource, List<Book> data) {
super(context, resource, data);
this.data = data;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new DynamicBookView(getContext());
}
Book bk = getItem(position);
DynamicBookView bookView = ((DynamicBookView) convertView); //so said view recycling
bookView.setBook(bk);
return convertView;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Book getItem(int position) {
return data.get(position);
}
public void swap(List<Book> books) {
data.clear();
data.addAll(books);
notifyDataSetChanged();
}
}
And the view:
public class DynamicBookView extends RelativeLayout {
private ImageView bImage;
private TextView bName;
private TextView bAuthor;
private TextView bDesc;
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
RequestHandle lastRequset;
public DynamicBookView(Context context) {
this(context, null);
}
public DynamicBookView(Context context, AttributeSet attrs) {
super(context, attrs);
View v = LayoutInflater.from(context).inflate(R.layout.common_dynamic_book, null);
addView(v, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
bImage = (ImageView) v.findViewById(R.id.book_image);
bName = (TextView) v.findViewById(R.id.book_name);
bAuthor = (TextView) v.findViewById(R.id.book_author);
bDesc = (TextView) v.findViewById(R.id.book_description);
}
public void setBook(final Book book) {
DynamicBookView.this.reset();
bName.setText(book.name);
bAuthor.setText(book.author);
bDesc.setText(book.description);
if (book.icon != null)
fetchImageForBook(book);
if (!book.loaded) {
// loadBook is an async operation which use `AsyncHttpClient` too
BookServer.getInstance().loadBook(book, new BookServer.BookServerLoadListener() {
@Override
protected void onComplete() {
setBook(book);
}
});
}
}
private void fetchImageForBook(Book book) {
//get from local:
if (lastRequset != null) {
lastRequset.cancel(true);
}
lastRequset = asyncHttpClient.get(book.icon, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
bImage.setImageBitmap(BitmapFactory.decodeByteArray(responseBody, 0, responseBody.length));
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
}
public void reset() {
if (lastRequset != null) {
lastRequset.cancel(true);
}
bName.setText("");
bAuthor.setText("");
bDesc.setText("");
bImage.setImageBitmap(null);
}
}
So I wonder if there is anything I can do to make the scroll more smooth?
Update:1
Once I remove the loading codes:
public void setBook(final Book book) {
DynamicBookView.this.reset();
bName.setText(book.name);
bAuthor.setText(book.author);
bDesc.setText(book.description);
}
Then the performance is acceptable.
And BTW, a request would take 2-3 seconds for get response.
Update:2
I am not sure if you notice that, each view for the list item will trigger two Async task:
public void setBook(final Book book) {
bName.setText(book.name);
bAuthor.setText(book.author);
bDesc.setText(book.description);
if (book.icon != null)
fetchImageForBook(book);
if (!book.loaded) {
// loadBook is an async operation which use `AsyncHttpClient` too
BookServer.getInstance().loadBook(book, new BookServer.BookServerLoadListener() {
@Override
protected void onComplete() {
setBook(book);
}
});
}
}
Generally, a Book
just contain the name
and id
, then I have to retrieve the detail of the book like author
description
and image
field. Then I will trigger another task to fetch the image. Now I have comment the line fetchImageForBook(book);
that's to say I just display the book without load the image, but the scrolling is still no-smooth.(I can not get the information once a time, since we do not serve the service)
Upvotes: 0
Views: 626
Reputation: 10203
You can use the ViewHolder pattern
for more performance optimization because it can be cached and avoid to call findViewById()
many times.
scrolling the ListView looks no-smooth
Keep in mind that if you just don't do heavy tasks while rendering view, your list view should be smoothed.
From your code, I can see your problem is that you create bitmaps from byte array many times and don't cache them.
bImage.setImageBitmap(BitmapFactory.decodeByteArray(responseBody, 0, responseBody.length));
bImage.setImageBitmap(null);
If you just get images from remote server, I recommend you use some image loader libraries like UIL, picasso, Volley... because they can cache bitmap into disk or memory to improve performance, or you can do it by yourself (download in background, cache and load into view)
Here is an example from my adapter that using android Universal Image Loader:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item_document, null);
viewHolder.imageView = (ImageView)convertView.findViewById(R.id.image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
ImageLoader.getInstance().displayImage(item.getImagePath()),viewHolder.imageView);
}
It's very simple and I even don't see any performance issue while scrolling listview.
Update:
From your BookServer source code, I think your problem is here:
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
//do the hard job here // what is this ???
listener.onComplete();
}
I don't know what hard job you did in this method, but the problem is this callback method run on UI thread. It causes performance issue while the asynctask doesn't because it run on background thread.
Upvotes: 2