1 在異步加載以前的代碼的和普通加載代碼同樣,只須要在GirdView的Adapter的public View getView(int position, View convertView, ViewGroupparent)方法使用異步加載的方式返回ImageView。 java
2 若是能把加載過的圖片給緩存起來,而不用每次都從sd卡上讀取,這樣效率應該會提升很多。因此能夠先建一個緩存類,MemoryCache,爲了能儘量緩存,又儘量的不拋出OOM的異常,可使用SoftReference<Bitmap>,軟引用來緩衝圖片。MemoryCache類的代碼以下: 緩存
- /**
- * 緩存類(圖片的路徑和圖片)
- */
- public class MemoryCache {
- private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();
- //獲取緩存圖片
- public Bitmap get(String path) {
- if(cache.get(path)==null){
- return null;
- }else{
- return cache.get(path).get();
- }
- }
-
- //新增緩存
- public void put(String url,Bitmap bitmap) {
- cache.put(url, new SoftReference<Bitmap>(bitmap));
- }
-
- //清空緩存
- public void clear() {
- cache.clear();
- }
- }
3 弄完了緩存類,接下來就能夠弄異步加載的類,這個類主要在維護一個Stack,每次向這個類請求一個ImageView時,就要傳入對應ImageView和圖片的路徑。異步類首先會根據圖片的路徑先去緩存中查找是否有緩存對應的BItMap,若是有就把他放到ImageView返回,若是沒有就把這個ImageView和圖片路徑放到Stack中,並喚醒加載圖片的線程。而加載圖片的線程(線程優先權低於UI線程),會無限循環查看Stack大小,若是爲0,就進入等待。若是不爲0,就依次出棧Stack中的元素進行處理。(感受像生產者-消費者模式)。 安全
3.1 接下來,就寫這個異步類的變量和構造函數了: 異步
- private MemoryCache cache;
- private PhotosStack photosStack;
- private LoadImageThread loadImageThread;
- //用來保存每月ImageView和對應應加載的圖片
- private Map<ImageView, String> viewPath=Collections.synchronizedMap(new HashMap<ImageView, String>());
- public LoadImage(Activity activity) {
- super();
- cache=new MemoryCache();
- photosStack=new PhotosStack();
- loadImageThread=new LoadImageThread(activity);
- //設置異步加載線程優先權低於UI線程
- loadImageThread.setPriority(Thread.NORM_PRIORITY);
- }
其中MemoryCache是緩存類,PhotosStack類中維護一個Stack,LoadImageThread類是負責異步加載圖片的。而viewPath這個變量是爲了要保證GridView對應Position放對應的ImageView,若是沒有這個變量,GridView中排列就會無序,在處理GridView的點擊事件時候,就很差處理。而對viewPath的操做的異步,因此就須要線程安全咯。 ide
PhotosStack代碼以下: 函數
- //維護須要被填充的Image和對應圖片路徑的棧
- class PhotosStack{
- Stack<MyPhoto> stack=new Stack<LoadImage.MyPhoto>();
- //移除指定ImageView
- public void remove(ImageView iView) {
- for(int i=0;i<stack.size();i++){
- if(stack.get(i).imageView==iView){
- stack.remove(i);
- break;
- }
- }
- }
- }
其中MyPhoto是一個照片的實體類,有兩個屬性,圖片的路徑和對應的ImageView。 this
- /**
- * 照片的實體類
- */
- class MyPhoto{
- public String path;
- public ImageView imageView;
- public MyPhoto(String path, ImageView imageView){
- this.path=path;
- this.imageView=imageView;
- }
- }
-
3.2 接下來就能夠實現給Adapter的調用的方法loadImage(Stringpath,ImageView iv ),接收兩參數——圖片路徑和對應的ImageView。這個方法,首先會去緩存中查看是否有緩存對應的圖片,若是有就把它設給ImageView。若是沒有,就先爲ImageView設個默認圖片,而後以同步塊(鎖爲PhotosStack中的stack)的方式加入PhotosStack中的stack中,並喚醒加載圖片的線程。最後還要判斷下加載圖片的線程是否已經被啓動了,若是沒有,就啓動。 url
- /**
- * 填充ImageView
- * @param activity
- * @param path 圖片的路徑
- * @param iv 被填充的ImageView
- */
- public void loadImage(String path,ImageView iv ) {
- viewPath.put(iv, path);
- Bitmap momeryBitmap=cache.get(path);
- if(momeryBitmap!=null){//有緩存這張圖片
- iv.setImageBitmap(momeryBitmap);
- }else{//沒有緩存
- iv.setImageResource(R.drawable.ic_launcher);
- //有能夠這個ImageVIew還沒出棧,因此須要先出棧
- photosStack.remove(iv);
- //因爲photosStack中的stack,出棧入棧操做時不會在異步的,因此須要在同步塊中完成出入棧,並以photosStack.stack做爲鎖
- synchronized (photosStack.stack) {
- photosStack.stack.push(new MyPhoto(path, iv));
- //喚醒持有鎖的線程
- photosStack.stack.notifyAll();
- }
- }
- if(loadImageThread.getState()==Thread.State.NEW){
- loadImageThread.start();
- }
-
-
- }
3.3 最後能夠實現異步加載圖片的方法了,主要是循環判斷stack中元素的數量,若是爲0 ,說明全部的圖片已經被加載完畢了,能夠進入等待狀態。若是不爲0,說明還有圖片等待加載,就依次出棧這些元素,依次加載圖片,並放到緩存中。代碼以下: spa
- //異步加載圖片的線程
- class LoadImageThread extends Thread{
- Activity activity;
-
- public LoadImageThread(Activity activity) {
- this.activity = activity;
- }
-
- @Override
- public void run() {
- while(true){
- try {
- if(photosStack.stack.size()>0){
- final MyPhoto photo;
- //獲得棧頂的MyPhoto
- synchronized (photosStack.stack) {
- photo= photosStack.stack.pop();
- }
- cache.put(photo.path,getSmallBitmap(photo.path) );
- String ivPathString=viewPath.get(photo.imageView);
- if(ivPathString!=null&& ivPathString.equalsIgnoreCase(photo.path)){
- Runnable runableRunnable=new Runnable() {
- @Override
- public void run() {
- photo.imageView.setImageBitmap(getSmallBitmap(photo.path));
- }
- };
- activity.runOnUiThread(runableRunnable);
- }
- }
- if(photosStack.stack.size()==0){
- synchronized (photosStack.stack) {
- photosStack.stack.wait();
- }
- }
- } catch (InterruptedException e) {
- // TODO 自動生成的 catch 塊
- e.printStackTrace();
- }
- }
- }
- }
-
其中縮小圖片方法getSmallBitmap(String path),代碼以下: .net
- //縮小圖片
- public Bitmap getSmallBitmap(String path) {
- Options options=new Options();
- options.inJustDecodeBounds=true;
- BitmapFactory.decodeFile(path,options);
- int REQUIRE_SIZE=80;
- int scare=1;
-
- while(true){
- if(options.outWidth<=REQUIRE_SIZE|| options.outHeight<=REQUIRE_SIZE){
- break;
- }else{
- options.outWidth=options.outWidth/2;
- options.outHeight=options.outHeight/2;
- scare++;
- }
- }
- Options newoptions=new Options();
- newoptions.inSampleSize=scare;
- return BitmapFactory.decodeFile(path, newoptions);