Android性能優化之提高ListView性能的技巧:
- 在adapter中的getView方法中儘量少使用邏輯
- 盡最大可能避免GC
- 滑動的時候不載入圖片
- 將ListView的scrollingCache和animateCache設置爲false
- item的佈局層級越少越好
- 使用ViewHolder
- 使用LRU緩存和異步加載
1.在adapter中的getView方法中儘量少使用邏輯
不要在你的getView()中寫過多的邏輯代碼,我們能夠將這些代碼放在別的地方。比如:
優化前的getView():
@Override
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
Object current_event = mObjects.get(position);
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.row_event, null);
holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//在這裏進行邏輯推斷。這是有問題的
if (doesSomeComplexChecking()) {
holder.ThreeDimention.setVisibility(View.VISIBLE);
} else {
holder.ThreeDimention.setVisibility(View.GONE);
}
// 這是設置image的參數,每次getView方法運行時都會運行這段代碼。這顯然是有問題的
RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
holder.EventPoster.setLayoutParams(imageParams);
return convertView;
}
優化後的getView():
@Override
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
Object object = mObjects.get(position);
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.row_event, null);
holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
//設置參數提到這裏,僅僅有第一次的時候會運行,之後會複用
RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
holder.EventPoster.setLayoutParams(imageParams);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 我們直接通過對象的getter方法取代剛纔那些邏輯推斷。那些邏輯推斷放到別的地方去運行了
holder.ThreeDimension.setVisibility(object.getVisibility());
return convertView;
}
2.盡最大可能避免GC
當你創建了大量的對象的時候。GC就會頻繁的運行。所以在getView()方法中不要創建非常多的對象。最好的優化是,不要在ViewHolder以外創建不論什麼對象。假設你的你的log裏面發現“GC has freed some memory”頻繁出現的話。那你的程序肯定有問題了。
你能夠檢查一下:
a) item佈局的層級是否太深
b) getView()方法中是否有大量對象存在
c) ListView的佈局屬性
3.滑動的時候不載入圖片
假設你的ListView中須要顯示從網絡上下載的圖片的話。我們不要在ListView滑動的時候載入圖片,那樣會使ListView變得卡頓,所以我們須要再監聽器裏面監聽ListView的狀態。假設滑動的時候,停止載入圖片,假設沒有滑動,則開始載入圖片
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView listView, int scrollState) {
//停止載入圖片
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
imageLoader.stopProcessingQueue();
} else {
//開始載入圖片
imageLoader.startProcessingQueue();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
4.將ListView的scrollingCache和animateCache設置爲false
scrollingCache: scrollingCache本質上是drawing cache,你能夠讓一個View將他自己的drawing保存在cache中(保存爲一個bitmap),這樣下次再顯示View的時候就不用重畫了,而是從cache中取出。默認情況下drawing cahce是禁用的。由於它太耗內存了,可是它確實比重畫來的更加平滑。
而在ListView中,scrollingCache是默認開啓的,我們能夠手動將它關閉。
animateCache: ListView默認開啓了animateCache,這會消耗大量的內存,因此會頻繁調用GC,我們能夠手動將它關閉掉
優化前的ListView
<
ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:divider="@color/list_background_color"
android:dividerHeight="0dp"
android:listSelector="#00000000"
android:smoothScrollbar="true"
android:visibility="gone" />
優化後的ListView
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@color/list_background_color"
android:dividerHeight="0dp"
android:listSelector="#00000000"
android:scrollingCache="false"
android:animationCache="false"
android:smoothScrollbar="true"
android:visibility="gone" />
5.降低item的佈局的深度
我們應該儘量降低item佈局深度,由於當滑動ListView的時候,這回直接導致測量與繪製,因此會浪費大量的時間。所以我們應該將一些不必要的佈局嵌套關係去掉。降低item佈局深度
6.使用ViewHolder
這個大家應該非常熟悉了,可是不要小看這個ViewHolder,它能夠大大提高我們ListView的性能
7.使用LRU緩存和異步加載
package com.example.mp3;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
@SuppressLint("NewApi")
public class music_Adapter extends ArrayAdapter<Music> {
private int resourceId;
private LruCache<Integer, Bitmap> mMemoryCache;
/**
* 圖片緩存技術的核心類,用於緩存所有下載好的圖片,在程序內存達到設定值時會將最少最近使用的圖片移除掉。
*/
public music_Adapter(Context view, int resource, List<Music> objects) {
super(view, resource, objects);
// TODO Auto-generated constructor stub
resourceId = resource;
// 獲取應用程序最大可用內存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<Integer, Bitmap>(cacheSize) {
@Override
protected int sizeOf(Integer key, Bitmap drawable) {
return drawable.getByteCount();
}
};
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//判斷是否緩存
if(convertView==null){
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(resourceId,parent, false);
holder.musicImage = (ImageView)convertView.findViewById(R.id.image_list);
holder.musicName = (TextView)convertView.findViewById(R.id.music_name);
holder.musicArtist = (TextView)convertView.findViewById(R.id.music_author);
convertView.setTag(holder);
} else {
//通過tag找到緩存的佈局
holder = (ViewHolder)convertView.getTag();
}
Music music = getItem(position);
//判斷專輯圖片是否爲空
// if(loadCoverFromMediaStore(music.getAlbumId()) != null) {
// holder.musicImage.setImageBitmap(loadCoverFromMediaStore(music.getAlbumId()));
// } else {
// holder.musicImage.setImageResource(R.drawable.qq);
// }
Bitmap drawable = getBitmapFromMemoryCache(music.getAlbumId());
if (drawable != null) {
holder.musicImage.setImageBitmap(drawable);
} else {
BitmapWorkerTask task = new BitmapWorkerTask(holder.musicImage);
task.execute(music.getAlbumId());
}
holder.musicName.setText(music.getTitle());
holder.musicArtist.setText(music.getArtist());
return convertView;
}
/**
* 將一張圖片存儲到LruCache中。
*
* @param key
* LruCache的鍵,這裏傳入專輯ID地址。
* @param drawable
* LruCache的值,這裏傳入從網絡上下載的BitmapDrawable對象。
*/
public void addBitmapToMemoryCache(Integer albumId, Bitmap drawable) {
if (getBitmapFromMemoryCache(albumId) == null&&albumId!=null&&drawable!=null) {
mMemoryCache.put(albumId, drawable);
}
}
/**
* 從LruCache中獲取一張圖片,如果不存在就返回null。
*
* @param key
* LruCache的鍵,這裏傳入圖片的URL地址。
* @return 對應傳入鍵的BitmapDrawable對象,或者null。
*/
public Bitmap getBitmapFromMemoryCache(Integer albumId) {
return mMemoryCache.get(albumId);
}
private Bitmap loadCoverFromMediaStore(int albumId){
Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(artworkUri, albumId);
ContentResolver resolver = getContext().getContentResolver();
InputStream is;
try {
is = resolver.openInputStream(uri);
} catch (FileNotFoundException e) {
//e.printStackTrace();
return null;
}
BitmapFactory.Options options = new Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeStream(is,null,options);
}
public final class ViewHolder{
public ImageView musicImage;
public TextView musicName;
public TextView musicArtist;
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private ImageView mImageView;
public BitmapWorkerTask(ImageView imageView) {
mImageView = imageView;
}
@Override
protected Bitmap doInBackground(Integer... params) {
Integer imageUrl = params[0];
// 在後臺開始下載圖片
Bitmap drawable = loadCoverFromMediaStore(imageUrl);
addBitmapToMemoryCache(imageUrl, drawable);
return drawable;
}
@Override
protected void onPostExecute(Bitmap drawable) {
if(drawable != null && mImageView != null) {
mImageView.setImageBitmap(drawable);
} else {
mImageView.setImageResource(R.drawable.qq);
}
}
}
}