Android ListView performance optimization solution
Preface:
For ListView, there is no stranger to everyone. As long as they have been Android Developers, how can they not use ListView?
If you have used ListView, why don't you care about its performance optimization?
How to optimize the performance of ListView is not only frequently asked during interviews (I interviewed several companies some time ago and all of them asked this question ), in addition, it is a very important part in actual projects. It even determines whether users like to accept your APP to some extent. (If your list slides off, I dare say many people will unmount it directly)
There are many solutions on the Internet for optimizing the performance of ListView. However, I have searched a lot of documents, but I feel very tired because many articles are vague and have no code instructions. You must know that the Code is the most direct to programmers !!!
1. Listview performance optimization solution
1). Reuse convertView
In getItemView, determine whether convertView is empty. If it is not empty, it can be reused. If you need to add listerner to the view in couvertview, the Code must be out of if (convertView = null.
2) asynchronously load images
If the item contains a webimage, it is best to load it asynchronously.
3). The image is not displayed during fast sliding.
When the list is quickly swiped (SCROLL_STATE_FLING), the image in the item or the view that consumes resources can be retrieved without being displayed. The image is in the other two States (SCROLL_STATE_IDLE and SCROLL_STATE_TOUCH_SCROLL ), the view is displayed.
Ii. How to optimize ListView2.1: first define a ListView
2.2 then we write a network request to obtain the json string of the network.
Here, we use httputil of the xutils framework, through which network requests can be conveniently carried out. As for the request url, we use the video data list interface provided by MOOC "http://www.imooc.com/api/teacher? Type = 4 & num = 30 ". Let's take a look at the tool class of an HTTP request I wrote:
Import android. content. context; import com. lidroid. xutils. httpUtils; import com. lidroid. xutils. exception. httpException; import com. lidroid. xutils. http. requestParams; import com. lidroid. xutils. http. responseInfo; import com. lidroid. xutils. http. callback. requestCallBack; import com. lidroid. xutils. http. client. httpRequest. httpMethod; import com. lidroid. xutils. util. logUtils;/*** network request tool class ** @ author lining */pub Lic class HttpUtil {/*** request root URL */public static final String BASE_URL = http://www.imooc.com/api/teacher? Type = 4 & num = 50; public static void sendRequest (final Context context, final HttpMethod method, RequestParams params, final IOAuthCallBack iOAuthCallBack) {HttpUtils http = new HttpUtils (); http. configCurrentHttpCacheExpiry (1000*5); // set the timeout value http. configTimeout (5*1000); http. configSoTimeout (5*1000); if (method = HttpMethod. GET) {http. configCurrentHttpCacheExpiry (5000); // set the cache to 5 seconds. If the last successful request is returned within 5 seconds Result.} Http. send (method, BASE_URL, params, new RequestCallBack
() {@ Override public void onStart () {LogUtils. d (method. name () + request is onStart .......);} @ Override public void onSuccess (ResponseInfo
ResponseInfo) {LogUtils. d (statusCode: + responseInfo. statusCode + -----> + responseInfo. result); iOAuthCallBack. getIOAuthCallBack (responseInfo. result); // call back data transmission using the interface} @ Override public void onFailure (HttpException error, String msg) {LogUtils. d (statusCode: + error. getExceptionCode () + -----> + msg); iOAuthCallBack. getIOAuthCallBack (FF); // call back data transmission using interfaces }});}}
There is nothing special about the tool class. It is nothing more than using HttpUtil in the Xutils framework to send network requests and obtain data. In the method parameters, we add an IOAuthCallBack callback interface, which is mainly used to call back the request result data between the Activity and the tool class.
/*** Data Request callback interface */public interface IOAuthCallBack {// successful public void getIOAuthCallBack (String result );}
Next, our Activity sends a network request, obtains json data, and calls back for processing:
Private void qryDataFromServer () {HttpUtil. sendRequest (this, HttpRequest. httpMethod. GET, null, this) ;}@ Override public void getIOAuthCallBack (String result) {RspData rspData = GsonUtil. getGson (). fromJson (result, RspData. class); // update the UI list KechengAdapter mAdapter = new KechengAdapter (this, rspData. data); listview. setAdapter (mAdapter );}
Here, GSON is used for parsing json data. There is nothing special to note. We can refer to the entity class code below:
public class RspData { public String status; public List
data; public String msg;}
public class KeCheng { public String id; public String name; public String picSmall; public String picBig; public String description; public String learner;}
2.3 After the set data is available, define the BaseAdapter
Before that, let's take a look at the layout file of the list item: list_item_kecheng.xml.
Next, let's take a good look at how the Adapter is defined:
import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class KechengAdapter extends BaseAdapter { private Context mContext; private LayoutInflater mInflater; private List
mDatas; public KechengAdapter(Context context, List
datas) { mContext = context; mInflater = LayoutInflater.from(mContext); mDatas = datas; } @Override public int getCount() { return (mDatas != null ? mDatas.size() : 0); } @Override public Object getItem(int position) { return (mDatas != null ? mDatas.get(position) : null); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_kecheng, null); holder = new ViewHolder(); holder.picBig = (ImageView) convertView.findViewById(R.id.picBig); holder.name = (TextView) convertView.findViewById(R.id.name); holder.description = (TextView) convertView.findViewById(R.id.description); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final KeCheng keCheng = mDatas.get(position); if (keCheng != null) { ImageLoaderUtil.getInstance().displayListItemImage(keCheng.picBig, holder.picBig); holder.name.setText(keCheng.name); holder.description.setText(keCheng.description); } return convertView; } static class ViewHolder { ImageView picBig; TextView name; TextView description; }}
The focus of ListView performance optimization is how to process BaseAdapterAnd look at the code above. In getView, we can judge whether convertView is empty. If it is not empty, it can be reused. How to reuse it?
We use the setTag method and getTag method of convertview to bind the data we want to display to convertview. If convertview is displayed for the first time, we will create a new Holder object and bind it to it. At last, return convertview to display it; if convertview is recycled, we do not need to create a new holder object. We only need to retrieve the original bound holder and add new data.
If you need to add listerner to the view in couvertview, the Code must be out of if (convertView = null.
People who have read the code carefully can find such a line of code,ImageLoaderUtil. getInstance (). displayListItemImage (keCheng. picBig, holder. picBig); this is the asynchronous Image loading framework Universal-Image-Loader used to load and cache network images asynchronously. (We strongly recommend that you use this open-source framework, we don't have to worry about loading cached network images anymore!
Let's take a look at how to configure this framework:
Import android. content. context; import android. graphics. bitmap; import android. widget. imageView; import com. nostra13.universalimageloader. cache. disc. impl. unlimitedDiscCache; import com. nostra13.universalimageloader. cache. disc. naming. md5FileNameGenerator; import com. nostra13.universalimageloader. cache. memory. impl. usingFreqLimitedMemoryCache; import com. nostra13.universalimageloader. core. displayImageOption S; import com. nostra13.universalimageloader. core. imageLoader; import com. nostra13.universalimageloader. core. imageLoaderConfiguration; import com. nostra13.universalimageloader. core. assist. queueProcessingType; import java. io. file;/*** configure the global Android-Universal-Image-Loader */public class ImageLoaderUtil {private static ImageLoaderUtil instance = null; private ImageLoader mImageLoader; // default picture privat in the list E DisplayImageOptions mListItemOptions; // private DisplayImageOptions mUserHeadOptions; private ImageLoaderUtil (Context context) {mImageLoader = ImageLoader. getInstance (); mListItemOptions = new DisplayImageOptions. builder () // set the image shown when the image Uri is null or incorrect. showImageForEmptyUri (R. mipmap. load_default_img ). showStubImage (R. mipmap. load_default_img) // sets the image displayed when an error occurs during image loading/decoding. showImageOnFail (R. mipmap. l Oad_default_img) // cache is loaded in the memory and disk when the image is loaded. cacheInMemory (). cacheOnDisc (). bitmapConfig (Bitmap. config. RGB_565 ). delayBeforeLoading (0, 300 ). build ();} public static ImageLoaderUtil getInstance () {return instance;} public synchronized static ImageLoaderUtil init (Context context) {if (instance = null) {instance = new ImageLoaderUtil (context);} File cacheDir = context. getExternalFilesDir (news/pictures ); ImageLoaderConfiguration config = new ImageLoaderConfiguration. builder (context ). threadPriority (Thread. NORM_PRIORITY-2 ). denyCacheImageMultipleSizesInMemory ()//. imageDownloader (imageDownloader ). imageDecoder (imageDecoder ). discCacheFileNameGenerator (new Md5FileNameGenerator ()). tasksProcessingOrder (QueueProcessingType. LIFO ). apsaradb for memoryCacheExtraOptions (360,360 ). memoryCache (new UsingFreqLimitedMem OryCache (4*1024*1024 )). discCache (new UnlimitedDiscCache (cacheDir )). build (); // Initialize ImageLoader with configuration. imageLoader. getInstance (). init (config); return instance;}/*** list image ** @ param uri * @ param imageView */public void displayListItemImage (String uri, ImageView imageView) {String strUri = (isEmpty (uri )?: Uri); mImageLoader. displayImage (strUri, imageView, mListItemOptions);} public ImageLoader getImageLoader () {return mImageLoader;} private boolean isEmpty (String str) {if (str! = Null & str. trim (). length ()> 0 &&! Str. Repeated signorecase (null) {return false;} return true ;}}
This is a Universal-Image-Loader tool class I have written. You can use it to download and cache images. Of course, you still need to initialize it before using it. We recommend that you initialize it in the Application:
public class MyApp extends Application { public static Context context; @Override public void onCreate() { super.onCreate(); context = this; ImageLoaderUtil.init(context); }}
2.4 Pause image loading when handling fast sliding
We know that when the list is quickly slide (SCROLL_STATE_FLING), the image in the item can be retrieved from the View that consumes resources, but it can not be displayed (because the sliding speed is too fast, we do not need to look at the picture ); in the other two States (SCROLL_STATE_IDLE and SCROLL_STATE_TOUCH_SCROLL), the views are displayed.
How can this problem be achieved? Here I still recommend that you use the methods that have been encapsulated by Universal-Image-Loader. (Of course, other frameworks, such as Xutils, also encapsulate related methods ). Com. nostra13.universalimageloader. core. assist. the PauseOnScrollListener listener has encapsulated a listener for image processing during scrolling. You only need to input the PauseOnScrollListener instance when setting a rolling listener for the ListView component. Here, let's take a look at the source code of PauseOnScrollListener:
public class PauseOnScrollListener implements OnScrollListener { private ImageLoader imageLoader; private final boolean pauseOnScroll; private final boolean pauseOnFling; private final OnScrollListener externalListener; public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) { this(imageLoader, pauseOnScroll, pauseOnFling, (OnScrollListener)null); } public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling, OnScrollListener customListener) { this.imageLoader = imageLoader; this.pauseOnScroll = pauseOnScroll; this.pauseOnFling = pauseOnFling; this.externalListener = customListener; } public void onScrollStateChanged(AbsListView view, int scrollState) { switch(scrollState) { case 0: this.imageLoader.resume(); break; case 1: if(this.pauseOnScroll) { this.imageLoader.pause(); } break; case 2: if(this.pauseOnFling) { this.imageLoader.pause(); } } if(this.externalListener != null) { this.externalListener.onScrollStateChanged(view, scrollState); } } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(this.externalListener != null) { this.externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } }}As you can see, PauseOnScrollListener implements the OnScrollListener interface, which is why the PauseOnScrollListener instance can be set to the ListView listener.
PauseOnScrollListener has two important constructor methods. The pauseOnScroll parameter controls the slow sliding of ListView, whether the GridView stops image loading, whether the pauseOnFling controls the violent sliding of ListView, and whether the GridView stops image loading. The other parameter OnScrollListener customListener can be used for developers to continue to process the corresponding sliding listening events, such as whether the list slides to the end.
Once we know how to use PauseOnScrollListener, we only need to set a simple listening code in the Activity:
listview.setOnScrollListener(new PauseOnScrollListener(ImageLoaderUtil.getInstance().getImageLoader(), false, true));
You must provide callback parameters for the sliding event to refresh or slide loading your project:
listview.setOnScrollListener(new PauseOnScrollListener(ImageLoaderUtil.getInstance().getImageLoader(), false, true, onScrollListener));
Private AbsListView. onScrollListener onScrollListener = new AbsListView. onScrollListener () {@ Override public void onScrollStateChanged (AbsListView view, int scrollState) {switch (scrollState) {case AbsListView. onScrollListener. SCROLL_STATE_TOUCH_SCROLL: // scroll the break after the touch; case AbsListView. onScrollListener. SCROLL_STATE_FLING: // rolling status break; case AbsListView. onScrollListener. SCROLL_STATE_IDLE: // idle status if (view. getLastVisiblePosition () = view. getCount ()-1) {System. out. println (************* scroll to the last ***************);} break ;}} @ Override public void onScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount ){}};
Well, the ListView is perfect. Let's take a look at its effect:
Conclusion:
This article mainly involves three aspects: 1. Reusing convertView; 2. Loading images asynchronously; 3. Not displaying images during quick sliding of ListView describes how to optimize the performance of ListView, this is the most common and important aspect. We recommend that you use it in your own project development to improve the ease of use of the list!
Of course, the article also mentions the use of two third-party frameworks: Xutils and Universal-Image-. These are two very useful frameworks. We suggest you also learn about them.
If you have other optimization solutions, we suggest you come up with them to learn and make progress together.