キャッシュ

20324 ワード

転載は出典を明記してください。http://blog.csdn.net/wei_チョングスching/articale/detail/50885900
今日はこれを非同期で画像を読み込んで修正しました。http://blog.csdn.net/wei_チョングスching/articale/detail/50880293
皆さんはご存知のように、画像のロードは非常に使う流量です。私達は一回だけロードしたいです。また閲覧する時は再ロードしなくてもいいです。
どうやって実現しますか
解決方法:
キャッシュを使う
Lru:Least Recently Usedは最近少なくともアルゴリズムを使います。
androidはLruCacheクラスを提供してこのキャッシュアルゴリズムを実現します。
どう使いますか
以下の変更はImageLoaderクラスにあります。
1.ImageLoaderクラスにメンバー変数を追加する
//  Cache        ,lruCache Map,          
	private LruCache<String,Bitmap> mCaches;
2.構成方法:
public ImageLoader(){
		//         LruCache
		//        ,
		int maxMemory = (int)Runtime.getRuntime().maxMemory();
		//      
		int cacheSixe = maxMemory / 4;
		mCaches = new LruCache<String, Bitmap>(cacheSixe){
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO Auto-generated method stub
				//            ,            
				return value.getByteCount();
			}
		};
	}
3.キャッシュにデータを保存してデータを読み取る方法を作成します。
/**
	 *      Cache  
	 * @param url
	 * @param bitmap
	 */

	public void addBitmapToCache(String url,Bitmap bitmap){
		if (getBitmapFromCache(url) == null) {
			mCaches.put(url, bitmap);
		}
	}
	/**
	 *  Cache      
	 */
	public Bitmap getBitmapFromCache(String url ){
		return mCaches.get(url);//lru   linkHashMap   
	}
4.ImageLodaer類のshowImageByAsyAryncTask()方法にいくつかの変更がある:
まずキャッシュに画像があるかどうかを判断します。あればキャッシュから取得します。ないならネットからダウンロードします。
/**
	 *   AsyncTask      
	 */

	public  void showImageByAsyncTask(ImageView imageView ,String url){
		//        
		Bitmap bitmap = getBitmapFromCache(url);
		if (bitmap == null) {
			//       ,           
			new NewsAsyncTask(imageView,url).execute(url);
		}else {
			//               
			imageView.setImageBitmap(bitmap);
		}

	}
以下の変更はImageLoaderの内部クラスNewsyncTaskにあります。
1.doInBackground(String...params)で変更:
ネットからダウンロードした画像をキャッシュに保存します。
@Override
		protected Bitmap doInBackground(String... params) {
			String url = params[0];
			Bitmap bitmap = getBitmapFromURL(params[0]);//       
			if(bitmap!=null){
				//      ,             
				addBitmapToCache(url, bitmap);
			}
			return bitmap;
		}
以下の変更はNewsdapterにあります。
必要に応じて画像を表示するごとにCacheオブジェクトを作成します。(キャッシュオブジェクトを作成するコードは構造関数の中にあります。newごとにImae Loaderが作成されます。新しいキャッシュ空間が作成されます。メッセージが表示されるたびにキャッシュ空間が作成されます。これはとても怖いです。これは私たちが望むものではありません。
私たちはキャッシュスペースが必要ですので、News Adapterクラスにメンバー変数を入れてもいいです。
コンストラクタでImageLoaderオブジェクトを作成し、そのメンバー変数を保存してください。
1.NewAdapter類にメンバー変数を加える
private ImageLoader mImageLoader;
は構成関数で初期化される。
public NewsAdapter(Context context,List<NewsBean> data){
		mList = data;
		mInflater = LayoutInflater.from(context);
		mImageLoader = new ImageLoader();//      lruCache
	}
get Viewメソッドでは、元のnew ImageLoader()をmImageLoaderに変換する。
mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, mList.get(position).newsIconUrl);
変更後のすべてのコード:
/**
 * ImageLoader            
 * @author Administrator
 *
 */


public class ImageLoader {
	private ImageView mImageView;
	private String mUrl;

	//  Cache        ,lruCache Map,          
	private LruCache<String,Bitmap> mCaches;

	public ImageLoader(){
		//         LruCache
		//        ,
		int maxMemory = (int)Runtime.getRuntime().maxMemory();
		//      
		int cacheSixe = maxMemory / 4;
		mCaches = new LruCache<String, Bitmap>(cacheSixe){
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO Auto-generated method stub
				//            ,            
				return value.getByteCount();
			}
		};
	}

	/**
	 *      Cache  
	 * @param url
	 * @param bitmap
	 */

	public void addBitmapToCache(String url,Bitmap bitmap){
		if (getBitmapFromCache(url) == null) {
			mCaches.put(url, bitmap);
		}
	}
	/**
	 *  Cache      
	 */
	public Bitmap getBitmapFromCache(String url ){
		return mCaches.get(url);//lru   linkHashMap   
	}
	/**
	 *          
	 */
	private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			super.handleMessage(msg);
			if(mImageView.getTag().equals(mUrl) ){
				mImageView.setImageBitmap((Bitmap) msg.obj);
			}

		};
	};
	public void showImageByThread(ImageView imageView,final String url){
		mImageView = imageView;
		mUrl = url;
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Bitmap bitmap = getBitmapFromURL(url);
				Message message = Message.obtain();
				message.obj = bitmap;
				mHandler.sendMessage(message);
			}
		}.start();
	}


	public Bitmap getBitmapFromURL(String urlString){
		Bitmap bitmap;
		InputStream is = null;
		try {
			URL url = new URL(urlString);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			is = new BufferedInputStream(connection.getInputStream());
			bitmap = BitmapFactory.decodeStream(is);
			connection.disconnect();

			//Thread.sleep(1000);//         ,        
			return bitmap;
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}


	/**
	 *   AsyncTask      
	 */

	public  void showImageByAsyncTask(ImageView imageView ,String url){
		//        
		Bitmap bitmap = getBitmapFromCache(url);
		if (bitmap == null) {
			//       ,           
			new NewsAsyncTask(imageView,url).execute(url);
		}else {
			//               
			imageView.setImageBitmap(bitmap);
		}

	}

	private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
		private ImageView mImageView;
		private String mUrl;

		public NewsAsyncTask(ImageView imageView,String url){
			mImageView = imageView;
			mUrl = url;
		}


		@Override
		protected Bitmap doInBackground(String... params) {
			String url = params[0];
			Bitmap bitmap = getBitmapFromURL(params[0]);//       
			if(bitmap!=null){
				//      ,             
				addBitmapToCache(url, bitmap);
			}
			return bitmap;
		}
		@Override
		protected void onPostExecute(Bitmap result) {
			// TODO Auto-generated method stub
			if (mImageView.getTag().equals(mUrl)) {
				mImageView.setImageBitmap(result);
			}

		}

	}
}
public class NewsAdapter extends BaseAdapter{

	private List<NewsBean> mList;
	private LayoutInflater mInflater;
	private ImageLoader mImageLoader;

	public NewsAdapter(Context context,List<NewsBean> data){
		mList = data;
		mInflater = LayoutInflater.from(context);
		mImageLoader = new ImageLoader();//      lruCache
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mList.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		ViewHolder viewHolder = null;
		if(convertView == null){
			viewHolder = new ViewHolder();
			convertView = mInflater.inflate(R.layout.item_layout, null);
			viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_newsimage);
			viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_newstitle);
			viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_newscontent);

			convertView.setTag(viewHolder);
		}else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);
		String url = mList.get(position).newsIconUrl;
		viewHolder.ivIcon.setTag(url);// imageView      url      ,  ListView               
		//           
		//new ImageLoader().showImageByThread(viewHolder.ivIcon, mList.get(position).newsIconUrl);
		//  AsyncTask      
		mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, mList.get(position).newsIconUrl);
		viewHolder.tvTitle.setText(mList.get(position).newstitle);
		viewHolder.tvContent.setText(mList.get(position).newscontent);
		return convertView;
	}
	class ViewHolder{
		public TextView tvTitle,tvContent;
		public ImageView ivIcon;

	}

}
引き続き最適化して効率を向上させます。
最適化スキーム:
ListViewスライドが停止したら、表示項目をロードします。
ListViewスライド時に、すべてのロードをキャンセルします。
News AdapterクラスにOSCrollListendインターフェースを実現させる。
インターフェースを実現する方法
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		//              ,            
		if(scrollState == SCROLL_STATE_IDLE){
			//       ,     
			mImageLoader.loadImages(mStart, mEnd);
		}else {
			mImageLoader.cancelAllTasks();
		}
	}
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		//          
		mStart = firstVisibleItem;
		mEnd = firstVisibleItem  + visibleItemCount;
		//        
		if(mFirstIn && visibleItemCount > 0){
			//        visibleItemCount   0 ,  item       ,       visibleItemCount > 0  
			mImageLoader.loadImages(mStart, mEnd);
			mFirstIn = false;
		}

	}
News Adapterにメンバー変数を追加します。
private int mStart,mEnd;
	public static String[] URLS;//       URL  

	private boolean mFirstIn;//          
構造の変更方法:
public NewsAdapter(Context context,List<NewsBean> data,ListView listview){
		mList = data;
		mInflater = LayoutInflater.from(context);

		mImageLoader = new ImageLoader(listview);//      lruCache
		URLS = new String[data.size()];
		for (int i = 0; i < data.size(); i++) {
			URLS[i] = data.get(i).newsIconUrl;
			// data     URL       
		}
		mFirstIn = true;
		//       
		listview.setOnScrollListener(this);// listView      
	}
ImageLayout類にメンバー変数を加える
	private ListView mListView;
	private Set<NewsAsyncTask> mTask;
構造方法を変更する:
public ImageLoader(ListView listview){

		mListView = listview ;
		mTask = new HashSet<NewsAsyncTask>();
		//         LruCache
		//        ,
		int maxMemory = (int)Runtime.getRuntime().maxMemory();
		//      
		int cacheSixe = maxMemory / 4;
		mCaches = new LruCache<String, Bitmap>(cacheSixe){
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO Auto-generated method stub
				//            ,            
				return value.getByteCount();
			}
		};
	}
は、指定されたシーケンスのピクチャをロードするためのlodeImage方法を作成する。
/**
	 *       start  end   
	 * @author Administrator
	 *
	 */
	public void loadImages(int start,int end){
		for (int i = start; i < end; i++) {
			String url  = NewsAdapter.URLS[i];
			//           
			Bitmap bitmap = getBitmapFromCache(url);
			if (bitmap == null) {
				//       ,           
				NewsAsyncTask task = new NewsAsyncTask(url);
				task.execute(url);
				mTask.add(task);
			}else {
				// listView   ImagView
				ImageView imageView = (ImageView) mListView.findViewWithTag(url);
				imageView.setImageBitmap(bitmap);
			}
		}
	}
構造の見直し
public NewsAsyncTask(String url){
			//mImageView = imageView;
			mUrl = url;
		}
onPostExecute方法を修正します。
@Override
		protected void onPostExecute(Bitmap result) {
			// TODO Auto-generated method stub
			ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
			if(imageView != null && result !=null){
				imageView.setImageBitmap(result);
			}
			mTask.remove(this);

		}
のすべてのコード:
package com.example.android;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;

/**
 * ImageLoader            
 * @author Administrator
 *
 */


public class ImageLoader {
	private ImageView mImageView;
	private String mUrl;

	//  Cache        ,lruCache Map,          
	private LruCache<String,Bitmap> mCaches;
	private ListView mListView;
	private Set<NewsAsyncTask> mTask;
	public ImageLoader(ListView listview){

		mListView = listview ;
		mTask = new HashSet<NewsAsyncTask>();
		//         LruCache
		//        ,
		int maxMemory = (int)Runtime.getRuntime().maxMemory();
		//      
		int cacheSixe = maxMemory / 4;
		mCaches = new LruCache<String, Bitmap>(cacheSixe){
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO Auto-generated method stub
				//            ,            
				return value.getByteCount();
			}
		};
	}

	/**
	 *      Cache  
	 * @param url
	 * @param bitmap
	 */

	public void addBitmapToCache(String url,Bitmap bitmap){
		if (getBitmapFromCache(url) == null) {
			mCaches.put(url, bitmap);
		}
	}
	/**
	 *  Cache      
	 */
	public Bitmap getBitmapFromCache(String url ){
		return mCaches.get(url);//lru   linkHashMap   
	}
	/**
	 *          
	 */
	private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			super.handleMessage(msg);
			if(mImageView.getTag().equals(mUrl) ){
				mImageView.setImageBitmap((Bitmap) msg.obj);
			}

		};
	};
	public void showImageByThread(ImageView imageView,final String url){
		mImageView = imageView;
		mUrl = url;
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Bitmap bitmap = getBitmapFromURL(url);
				Message message = Message.obtain();
				message.obj = bitmap;
				mHandler.sendMessage(message);
			}
		}.start();
	}


	public Bitmap getBitmapFromURL(String urlString){
		Bitmap bitmap;
		InputStream is = null;
		try {
			URL url = new URL(urlString);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			is = new BufferedInputStream(connection.getInputStream());
			bitmap = BitmapFactory.decodeStream(is);
			connection.disconnect();

			//Thread.sleep(1000);//         ,        
			return bitmap;
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}


	/**
	 *   AsyncTask      
	 */

	public  void showImageByAsyncTask(ImageView imageView ,String url){
		//        
		Bitmap bitmap = getBitmapFromCache(url);
		if (bitmap == null) {
			//       ,       
			imageView.setImageResource(R.drawable.ic_launcher);
		}else {
			//               
			imageView.setImageBitmap(bitmap);
		}

	}
	/**
	 *         
	 */
	public void cancelAllTasks(){
		if(mTask!=null){
			for (NewsAsyncTask task:mTask) {
				task.cancel(false);
			}
		}
	}
	/**
	 *       start  end   
	 * @author Administrator
	 *
	 */
	public void loadImages(int start,int end){
		for (int i = start; i < end; i++) {
			String url  = NewsAdapter.URLS[i];
			//           
			Bitmap bitmap = getBitmapFromCache(url);
			if (bitmap == null) {
				//       ,           
				NewsAsyncTask task = new NewsAsyncTask(url);
				task.execute(url);
				mTask.add(task);
			}else {
				// listView   ImagView
				ImageView imageView = (ImageView) mListView.findViewWithTag(url);
				imageView.setImageBitmap(bitmap);
			}
		}
	}
	private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
		//private ImageView mImageView;
		private String mUrl;

		public NewsAsyncTask(String url){
			//mImageView = imageView;
			mUrl = url;
		}


		@Override
		protected Bitmap doInBackground(String... params) {
			String url = params[0];
			Bitmap bitmap = getBitmapFromURL(params[0]);//       
			if(bitmap!=null){
				//      ,             
				addBitmapToCache(url, bitmap);
			}
			return bitmap;
		}
		@Override
		protected void onPostExecute(Bitmap result) {
			// TODO Auto-generated method stub
			ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
			if(imageView != null && result !=null){
				imageView.setImageBitmap(result);
			}
			mTask.remove(this);

		}

	}
}
package com.example.android;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class NewsAdapter extends BaseAdapter implements OnScrollListener{

	private List<NewsBean> mList;
	private LayoutInflater mInflater;
	private ImageLoader mImageLoader;

	private int mStart,mEnd;
	public static String[] URLS;//       URL  

	private boolean mFirstIn;//          


	public NewsAdapter(Context context,List<NewsBean> data,ListView listview){
		mList = data;
		mInflater = LayoutInflater.from(context);

		mImageLoader = new ImageLoader(listview);//      lruCache
		URLS = new String[data.size()];
		for (int i = 0; i < data.size(); i++) {
			URLS[i] = data.get(i).newsIconUrl;
			// data     URL       
		}
		mFirstIn = true;
		//       
		listview.setOnScrollListener(this);// listView      
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mList.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		ViewHolder viewHolder = null;
		if(convertView == null){
			viewHolder = new ViewHolder();
			convertView = mInflater.inflate(R.layout.item_layout, null);
			viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_newsimage);
			viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_newstitle);
			viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_newscontent);

			convertView.setTag(viewHolder);
		}else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);
		String url = mList.get(position).newsIconUrl;
		viewHolder.ivIcon.setTag(url);// imageView      url      ,  ListView               
		//           
		//new ImageLoader().showImageByThread(viewHolder.ivIcon, mList.get(position).newsIconUrl);
		//  AsyncTask      
		mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, mList.get(position).newsIconUrl);
		viewHolder.tvTitle.setText(mList.get(position).newstitle);
		viewHolder.tvContent.setText(mList.get(position).newscontent);
		return convertView;
	}
	class ViewHolder{
		public TextView tvTitle,tvContent;
		public ImageView ivIcon;

	}
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		//              ,            
		if(scrollState == SCROLL_STATE_IDLE){
			//       ,     
			mImageLoader.loadImages(mStart, mEnd);
		}else {
			mImageLoader.cancelAllTasks();
		}
	}
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		//          
		mStart = firstVisibleItem;
		mEnd = firstVisibleItem  + visibleItemCount;
		//        
		if(mFirstIn && visibleItemCount > 0){
			//        visibleItemCount   0 ,  item       ,       visibleItemCount > 0  
			mImageLoader.loadImages(mStart, mEnd);
			mFirstIn = false;
		}

	}

}
まとめ:
非同期ローディングにより、UIスレッドのブロックを避ける
LruCacheを通じて、ダウンロード済みの画像をメモリに保存します。
ListViewスライド状態を判断することで、いつ画像を読み込むかを決定します。
ListIViewだけでなく、任意のコントロールでも非同期ローディングができます。