로 컬 앨범 그림 을 효율적으로 불 러 오 는 ImageLoader 클래스

17133 단어 그림.
우리 앨범 의 그림 이 수천 장 이 있 을 때,당신 은 스크롤 바 의 끝 부분 을 빠르게 끌 어 당 깁 니 다.어떻게 해야만 그림 로드 의 유창 성과 OOM 을 피 할 수 있 습 니까?
         1.Lru 알고리즘 을 사용 하여 그림 을 캐 시 하여 유창 성 을 확보 하고 OOM 을 피한다.
         2.이미지 로드 는 반드시 비동기 적 으로 진행 되 어야 합 니 다.그러면 다 중 스 레 드 의 병행 과 관련 되 고 스 레 드 탱크 를 사용 하여 작업 을 스케줄 링 합 니 다.
         3.안 드 로 이 드 내부 의 비동기 메시지 메커니즘 Looper+Handler 를 사용 하여 taskQueue 에 대해 폴 링 을 수행 합 니 다.
1.이미지 로 더,task 작업 열 이 자주 호출 되 기 때문에 하나의 예 모드 로 이미지 로 더 류 를 만 들 고 필요 한 구성원 변 수 를 설명 합 니 다.
/**
 * @author Administrator
 *	     ,task       ,         
 */
public class ImageLoader {
	private static ImageLoader instance;
	/**
	 *          
	 */
	private LruCache<String, Bitmap> mLruCache;
	/**
	 *    
	 */
	private ExecutorService mThreadPool;
	private static final int DEFAULT_THREAD_COUNT=1;
	/**
	 *       
	 */
	private static Type mType=Type.LIFO;
	/**
	 *     
	 */
	private LinkedList<Runnable> mTaskQueue;
	/**
	 *       
	 */
	private Thread mPoolThread;
	private Handler mPoolThreadHandler;
	/**
	 *UI   Handler
	 */
	private Handler mUIHandler;
	//     
	public enum Type{
		FIFO,LIFO;
	}
	/**
	 *   ImageLoader     
	 * @return
	 */
	public static ImageLoader getInstance(){
		//      if  ,           
		//                ,           
		//       
		if(instance==null){
			synchronized (ImageLoader.class) {
				if(instance==null){
					instance=new ImageLoader(DEFAULT_THREAD_COUNT,mType);
				}
			}
		}
		return instance;
	}

2.멤버 변수 초기 화
private ImageLoader(int threadCount,Type type){
		init(threadCount,type);
	}
	private Semaphore mSemaphorePoolThreadHandler=new Semaphore(0);
	private Semaphore mSemaphoreThreadPool;
	/**
	 *                
	 * @param threadCount
	 * @param type
	 */
	private void init(int threadCount,Type type) {
		//         
		mPoolThread=new Thread(){
			@Override
			public void run() {
				Looper.prepare();//            mtaskQueue    
				mPoolThreadHandler=new Handler(){
					@Override
					public void handleMessage(Message msg) {
						//            
						mThreadPool.execute(getTask());
						
					}
				};
				mSemaphorePoolThreadHandler.release();//handler     ,       
				Looper.loop();
			}
		};
		//     
		mPoolThread.start();
		mTaskQueue=new LinkedList<Runnable>();//    
		mThreadPool=Executors.newFixedThreadPool(threadCount);//      
		int maxSize=(int) Runtime.getRuntime().maxMemory();
		int cacheSize=maxSize/8;
		mLruCache=new LruCache<String, Bitmap>(cacheSize){

			@Override
			protected int sizeOf(String key, Bitmap value) {
				return value.getRowBytes()*value.getHeight();
			}
			
		};
		mType=type;
		mSemaphoreThreadPool=new Semaphore(threadCount);		
	}

3.작성 및 새로운 방법 loadImage()
/**
	 *          
	 * @param path
	 * @param imageView
	 */
	public void loadImage(final String path,final ImageView imageView){
		imageView.setTag(path);//    ,             
		mUIHandler=new Handler(){
			@Override
			public void handleMessage(Message msg) {
				ImageHolder holder=(ImageHolder) msg.obj;
				Bitmap bitmap=holder.mBitmap;
				ImageView imageView=holder.mImageView;
				String url=holder.path;
				if(imageView.getTag().toString().equals(url)){
					imageView.setImageBitmap(bitmap);
				}
			}
		};
		
		Bitmap mBitmap=getBitmapFromLruCache(path);
		
		if(mBitmap!=null){
			refreshHandler(path, imageView, mBitmap);
		}
		else{
			addTask(imageView,path);
		}
		
	}
	private void addTask(final ImageView imageView,final String path) {
		mTaskQueue.add(new Runnable() {
			
			@Override
			public void run() {
				//         
				//1.  ImageView      
				ImageSize imageSize = getImageSize(imageView);
				//2.      
				Bitmap mBitmap = compressBitmap(imageSize,path);
				//3.      LruCache  
				if(mLruCache.get(path)!=null){
					mLruCache.put(path, mBitmap);
				}
				//    , UiHandler      imageview  
				refreshHandler(path, imageView, mBitmap);
				//    ,      ,               
				mSemaphoreThreadPool.release();
			}
		});
		try {
			if(mPoolThreadHandler==null){
				mSemaphorePoolThreadHandler.acquire();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//    ,             
		mPoolThreadHandler.sendEmptyMessage(0X110);
		
	}
	/**
	 *  ImageView path Bitmap      handler    
	 * @param path
	 * @param imageView
	 * @param mBitmap
	 */
	private void refreshHandler(final String path, final ImageView imageView, Bitmap mBitmap) {
		Message message=Message.obtain();
		ImageHolder holder=new ImageHolder();
		holder.mBitmap=mBitmap;
		holder.mImageView=imageView;
		holder.path=path;
		message.obj=holder;
		mUIHandler.sendMessage(message);
	}
	/**
	 *     
	 * @return
	 */
	private Bitmap compressBitmap(ImageSize imageSize,String path){
		BitmapFactory.Options option=new BitmapFactory.Options();
		option.inJustDecodeBounds=true;//           ,            
		BitmapFactory.decodeFile(path, option);
		int outWidth = option.outWidth;
		int outHeight = option.outHeight;
		int width=imageSize.width;
		int height=imageSize.height;
		int inSimpleSize=1;
		if(outHeight>height||outWidth>height){
			int widthRadio=Math.round(outWidth*1.0f/width);
			int heightRadio=Math.round(outHeight*1.0f/height);
			inSimpleSize=Math.max(widthRadio, heightRadio);
		}
		option.inJustDecodeBounds=false;
		option.inSampleSize=inSimpleSize;
		
		return BitmapFactory.decodeFile(path, option);
	}
	/**
	 *   ImageView            
	 * @param iv
	 * @return
	 */
	private ImageSize getImageSize(ImageView iv){
		LayoutParams lp = iv.getLayoutParams();
		//       
		DisplayMetrics dm = iv.getContext().getResources().getDisplayMetrics();
		int width=iv.getWidth();
		if(width<=0){
			width=lp.width;
		}
		if(width<=0){
			//      ImageView MaxWidth MaxHeight     
			width=getMaxValueFromInvoke(iv,"mMaxWidth");
		}
		if(width<=0){
			width=dm.widthPixels;
		}
		
		int height=iv.getHeight();
		if(height<=0){
			height=lp.width;
		}
		if(height<=0){
			height=getMaxValueFromInvoke(iv,"mMaxHeight");
		}
		if(height<=0){
			height=dm.heightPixels;
		}
		return new ImageSize(width, height);
	}
	/**
	 *       ImageView MaxWidth MaxHeight     
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	private int getMaxValueFromInvoke(Object obj,String fieldName){
		int value=0;
		try {
			Field field = ImageView.class.getDeclaredField(fieldName);
			field.setAccessible(true);
			int fieldValue = field.getInt(obj);
			if(fieldValue>0&&fieldValue<Integer.MAX_VALUE){
				value=fieldValue;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return value;
	}
	/**
	 *          
	 * @author Administrator
	 *
	 */
	private class ImageSize{
		int width;
		int height;
		public ImageSize(int width,int height){
			this.width=width;
			this.height=height;
		}
	}
	//handler   msg.obj  
	private class ImageHolder{
		Bitmap mBitmap;
		ImageView mImageView;
		String path;
	}
	//        
	private Bitmap getBitmapFromLruCache(String key) {
		
		return mLruCache.get(key);
	}
	

ImageView 의 최대 너비 와 높이 를 가 져 올 때 낮은 버 전 을 호 환 하기 위해 getMaxWidth()라 는 api 를 사용 하지 않 고 반사 적 으로 가 져 온 것 입 니 다.
그 중에서 배경 폴 링 스 레 드 가 mThreadPoolHandler 를 초기 화하 지 않 았 을 때 mThreadPoolHandler 는 loadImage()방법 에서 호출 될 수 있 기 때문에 빈 포인터 의 위험 이 존재 합 니 다.
병발 위험 을 확보 하기 위해 자바 가 제공 하 는 병발 신 호 량 의 클래스 Semaphore   초기 화 할 때 구조 방법 에 몇 개의 허가증 을 지정 합 니 다.
두 가지 방법 이 있다.
          1.acquire()는 실행 할 때마다 하나의 허가증 을 소모 하지만 허가증 이 0 일 때 스 레 드 를 차단 합 니 다.Semaphore 까지.   release()를 다시 호출 하여 허가증 을 얻 고 차단 을 끝 냅 니 다.
           2.release()를 실행 할 때마다 허가증 을 받 습 니 다.
다음은 제 가 모든 종류의 코드 를 다 붙 여 드릴 게 요.
package com.example.fandayimageloader.imageloader;

import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
/**
 *       mPoolThreadHandler          ,   addTask mTaskQueue      ,    null painter exception
 *         java   Semaphore,         。     ,           。    ,             acquire(),
 *         。   release()       ,                。  ,          ,Semaphore              ,
 *         。              ,     。  acquire() release()         。
 *       acquire()       , release()      ,    0   ,    acquire()    
 * public class SemaphoreTest {
 *
 *	     public static void main(String[] args) {  
 *	        //     
 *	        ExecutorService exec = Executors.newCachedThreadPool();  
 *	        //   5        
 *	        final Semaphore semp = new Semaphore(5);  
 *	        //   20       
 *	        for (int index = 0; index < 20; index++) {
 *	            final int NO = index;  
 *	            Runnable run = new Runnable() {  
 *	                public void run() {  
 *	                    try {  
 * 	                        //      
 *	                        semp.acquire();  
 *                       System.out.println("Accessing: " + NO);  
 *	                        Thread.sleep((long) (Math.random() * 10000));  
 *	                        //     ,   ,         ,         5   ,        
 *	                        semp.release();  
 *	                    } catch (InterruptedException e) {  
 *	                    }  
 *	                }  
 *	            };  
 *	            exec.execute(run);  
 *	        }  
 *	        //       
 *	        exec.shutdown();  
 *	    }  
 *	}
 */

/**
 * @author Administrator
 *	     ,task       ,         
 */
public class ImageLoader {
	private static ImageLoader instance;
	/**
	 *          
	 */
	private LruCache<String, Bitmap> mLruCache;
	/**
	 *    
	 */
	private ExecutorService mThreadPool;
	private static final int DEFAULT_THREAD_COUNT=1;
	/**
	 *       
	 */
	private static Type mType=Type.LIFO;
	/**
	 *     
	 */
	private LinkedList<Runnable> mTaskQueue;
	/**
	 *       
	 */
	private Thread mPoolThread;
	private Handler mPoolThreadHandler;
	/**
	 *UI   Handler
	 */
	private Handler mUIHandler;
	//     
	public enum Type{
		FIFO,LIFO;
	}
	/**
	 *   ImageLoader     
	 * @return
	 */
	public static ImageLoader getInstance(){
		//      if  ,           
		//                ,           
		//       
		if(instance==null){
			synchronized (ImageLoader.class) {
				if(instance==null){
					instance=new ImageLoader(DEFAULT_THREAD_COUNT,mType);
				}
			}
		}
		return instance;
	}
	
	private ImageLoader(int threadCount,Type type){
		init(threadCount,type);
	}
	private Semaphore mSemaphorePoolThreadHandler=new Semaphore(0);
	private Semaphore mSemaphoreThreadPool;
	/**
	 *                
	 * @param threadCount
	 * @param type
	 */
	private void init(int threadCount,Type type) {
		//         
		mPoolThread=new Thread(){
			@Override
			public void run() {
				Looper.prepare();//            mtaskQueue    
				mPoolThreadHandler=new Handler(){
					@Override
					public void handleMessage(Message msg) {
						//            
						mThreadPool.execute(getTask());
						
					}
				};
				mSemaphorePoolThreadHandler.release();//handler     ,       
				Looper.loop();
			}
		};
		//     
		mPoolThread.start();
		mTaskQueue=new LinkedList<Runnable>();//    
		mThreadPool=Executors.newFixedThreadPool(threadCount);//      
		int maxSize=(int) Runtime.getRuntime().maxMemory();
		int cacheSize=maxSize/8;
		mLruCache=new LruCache<String, Bitmap>(cacheSize){

			@Override
			protected int sizeOf(String key, Bitmap value) {
				return value.getRowBytes()*value.getHeight();
			}
			
		};
		mType=type;
		mSemaphoreThreadPool=new Semaphore(threadCount);		
	}
	/**
	 *         Type      
	 * @return
	 */
	public Runnable getTask(){
		try {
			//             ,       ,      
			mSemaphoreThreadPool.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if(mType==Type.FIFO){
			return mTaskQueue.removeFirst();
		}
		else if(mType==Type.LIFO){
			return mTaskQueue.removeLast();
		}
		return null;
	}
	
	/**
	 *          
	 * @param path
	 * @param imageView
	 */
	public void loadImage(final String path,final ImageView imageView){
		imageView.setTag(path);//    ,             
		mUIHandler=new Handler(){
			@Override
			public void handleMessage(Message msg) {
				ImageHolder holder=(ImageHolder) msg.obj;
				Bitmap bitmap=holder.mBitmap;
				ImageView imageView=holder.mImageView;
				String url=holder.path;
				if(imageView.getTag().toString().equals(url)){
					imageView.setImageBitmap(bitmap);
				}
			}
		};
		
		Bitmap mBitmap=getBitmapFromLruCache(path);
		
		if(mBitmap!=null){
			refreshHandler(path, imageView, mBitmap);
		}
		else{
			addTask(imageView,path);
		}
		
	}
	private void addTask(final ImageView imageView,final String path) {
		mTaskQueue.add(new Runnable() {
			
			@Override
			public void run() {
				//         
				//1.  ImageView      
				ImageSize imageSize = getImageSize(imageView);
				//2.      
				Bitmap mBitmap = compressBitmap(imageSize,path);
				//3.      LruCache  
				if(mLruCache.get(path)!=null){
					mLruCache.put(path, mBitmap);
				}
				//    , UiHandler      imageview  
				refreshHandler(path, imageView, mBitmap);
				//    ,      ,               
				mSemaphoreThreadPool.release();
			}
		});
		try {
			if(mPoolThreadHandler==null){
				mSemaphorePoolThreadHandler.acquire();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//    ,             
		mPoolThreadHandler.sendEmptyMessage(0X110);
		
	}
	/**
	 *  ImageView path Bitmap      handler    
	 * @param path
	 * @param imageView
	 * @param mBitmap
	 */
	private void refreshHandler(final String path, final ImageView imageView, Bitmap mBitmap) {
		Message message=Message.obtain();
		ImageHolder holder=new ImageHolder();
		holder.mBitmap=mBitmap;
		holder.mImageView=imageView;
		holder.path=path;
		message.obj=holder;
		mUIHandler.sendMessage(message);
	}
	/**
	 *     
	 * @return
	 */
	private Bitmap compressBitmap(ImageSize imageSize,String path){
		BitmapFactory.Options option=new BitmapFactory.Options();
		option.inJustDecodeBounds=true;//           ,            
		BitmapFactory.decodeFile(path, option);
		int outWidth = option.outWidth;
		int outHeight = option.outHeight;
		int width=imageSize.width;
		int height=imageSize.height;
		int inSimpleSize=1;
		if(outHeight>height||outWidth>height){
			int widthRadio=Math.round(outWidth*1.0f/width);
			int heightRadio=Math.round(outHeight*1.0f/height);
			inSimpleSize=Math.max(widthRadio, heightRadio);
		}
		option.inJustDecodeBounds=false;
		option.inSampleSize=inSimpleSize;
		
		return BitmapFactory.decodeFile(path, option);
	}
	/**
	 *   ImageView            
	 * @param iv
	 * @return
	 */
	private ImageSize getImageSize(ImageView iv){
		LayoutParams lp = iv.getLayoutParams();
		//       
		DisplayMetrics dm = iv.getContext().getResources().getDisplayMetrics();
		int width=iv.getWidth();
		if(width<=0){
			width=lp.width;
		}
		if(width<=0){
			//      ImageView MaxWidth MaxHeight     
			width=getMaxValueFromInvoke(iv,"mMaxWidth");
		}
		if(width<=0){
			width=dm.widthPixels;
		}
		
		int height=iv.getHeight();
		if(height<=0){
			height=lp.width;
		}
		if(height<=0){
			height=getMaxValueFromInvoke(iv,"mMaxHeight");
		}
		if(height<=0){
			height=dm.heightPixels;
		}
		return new ImageSize(width, height);
	}
	/**
	 *       ImageView MaxWidth MaxHeight     
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	private int getMaxValueFromInvoke(Object obj,String fieldName){
		int value=0;
		try {
			Field field = ImageView.class.getDeclaredField(fieldName);
			field.setAccessible(true);
			int fieldValue = field.getInt(obj);
			if(fieldValue>0&&fieldValue<Integer.MAX_VALUE){
				value=fieldValue;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return value;
	}
	/**
	 *          
	 * @author Administrator
	 *
	 */
	private class ImageSize{
		int width;
		int height;
		public ImageSize(int width,int height){
			this.width=width;
			this.height=height;
		}
	}
	//handler   msg.obj  
	private class ImageHolder{
		Bitmap mBitmap;
		ImageView mImageView;
		String path;
	}
	//        
	private Bitmap getBitmapFromLruCache(String key) {
		
		return mLruCache.get(key);
	}
	
}

마지막 으로 ImageLoader 를 외부 에서 호출 할 때 Type 속성 과 스 레 드 탱크 의 최대 스 레 드 개 수 를 지정 하려 면 getInstance 방법 을 추가 하여 다시 불 러 올 수 있 습 니 다.
/**
	 *   ImageLoader        
	 * @return
	 */
	public static ImageLoader getInstance(int threadCount,Type type){
		//      if  ,           
		//                ,           
		//       
		if(instance==null){
			synchronized (ImageLoader.class) {
				if(instance==null){
					instance=new ImageLoader(threadCount,type);
				}
			}
		}
		return instance;
	}

프로젝트 원본 다운로드 주소:원본 다운로드

좋은 웹페이지 즐겨찾기