Android性能優化之提高ListView性能的技巧

Android性能優化之提高ListView性能的技巧:

  1. 在adapter中的getView方法中儘量少使用邏輯
  2. 盡最大可能避免GC
  3. 滑動的時候不載入圖片
  4. 將ListView的scrollingCache和animateCache設置爲false
  5. item的佈局層級越少越好
  6. 使用ViewHolder
  7. 使用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);
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章