Picasso框架源碼解析(一)。

相信做android開發的都會用到Picasso、Universal-ImageLoader等,和圖片相關的框架。這些開源的框架在很多情況下都可以使用,這個時候可能就有問題了,我該怎麼選擇框架呢? 本人曾經或者現在也會遇到這樣的苦惱,(有時候就像剛學android一樣,當時的苦惱是如何實現這個功能)現在是用什麼方式實現來說“性價比最好”。

對於這個問題,只有唯一解。那就是對這幾個框架的源碼進行專研了。理解了其中的原理和邏輯,那麼這些問題就不在是爲什麼了。

先說說我在項目中關於圖片常用到的兩個框架Picasso和UIL(Universal-ImageLoader)
先分析源碼,之後在總結他們的適用場景。

Picasso

簡介

Images add much-needed context and visual flair to Android applications. Picasso allows for hassle-free image loading in your application—often in one line of code!
假如你想在你的安卓程序中添加你所需要的(帶上下文引用的視覺風格的)圖片,那麼使用Picasso框架僅僅一行代碼,就可以達到你所需要的效果,而且不需要考慮什麼oom之類的問題。

這是Picasso官網對其的說明,我的總結是 簡單、粗暴,一行代碼。

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Picasso的特點

  • Handling ImageView recycling and download cancelation in an adapter.
    對圖像自動進行回收以及在適配的時候getView()方法裏面可以取消加載圖片(可以用在listview快速滾動,不加載等場景)
  • Complex image transformations with minimal memory use.
    對於複雜圖片用最小的內存去轉換。
  • Automatic memory and disk caching.
    自動的對圖片進行磁盤緩存和內存。

看到這裏相信你對Picasso有了一些印象了吧,接下來分析源碼

Picasso的使用

Resources, assets, files, content providers are all supported as image sources.
一般我們的image可以是Resources, assets, files, content providers

Picasso.with(context).load(R.drawable.landing_screen).into(imageView1);
Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);
Picasso.with(context).load(new File(...)).into(imageView3);

     picasso_iv = (ImageView) findViewById(R.id.picasso_iv);
     Picasso.with(this).load("圖片").transform(new Transformation() {
            @Override
            public Bitmap transform(Bitmap source) {
                int size = Math.min(source.getWidth(), source.getHeight());
                int x = (source.getWidth() - size) / 4;
                int y = (source.getHeight() - size) / 4;
                Bitmap result = Bitmap.createBitmap(source, x, y, size, size);
                if (result != source) {
                    source.recycle();
                }
                return result;
            }
            @Override
            public String key() {
                return "square()";
            }
        }).placeholder(R.mipmap.ic_launcher).into(picasso_iv);

Picasso源碼

     我們看到 Picasso.with(this),那麼Picasso是如我們所料的單例模式嗎?
public class Picasso {
....java
//volatile關鍵字修飾的對象每次都是都是從主存中獲取,而不是從各個子線程中獲取。也就是說它每次都是拿到最新的值,但是它不保證執行線程的有序性。
static volatile Picasso singleton = null;
...
 public static Picasso with(Context context) {
        if(singleton == null) {
            Class var1 = Picasso.class;
            synchronized(Picasso.class) {
                if(singleton == null) {
                    //這裏創建了一個Picasso實例
                    singleton = (new Picasso.Builder(context)).build();
                }
            }
        }

        return singleton;
    }
}
...

Volatile關鍵字

接下來看Picasso.Builder(context)).build()

  public Picasso build() {
         //...
         //這裏最後調用構造函數創建了一個Picasso對象
   return new Picasso(context, dispatcher, this.cache, this.listener, this.transformer,   this.requestHandlers, stats, this.defaultBitmapConfig, this.indicatorsEnabled,   this.loggingEnabled);
        }
    }
接着看
Picasso.with(this).load("圖片"),拿到Picasso對象後進行load()操作
  public RequestCreator load(Uri uri) {
        return new RequestCreator(this, uri, 0);
    }

    public RequestCreator load(String path) {
        if(path == null) {
            return new RequestCreator(this, (Uri)null, 0);
        } else if(path.trim().length() == 0) {
            throw new IllegalArgumentException("Path must not be empty.");
        } else {
            return this.load(Uri.parse(path));
        }
    }

    public RequestCreator load(File file) {
        return file == null?new RequestCreator(this, (Uri)null, 0):this.load(Uri.fromFile(file));
    }

    public RequestCreator load(int resourceId) {
        if(resourceId == 0) {
            throw new IllegalArgumentException("Resource ID must not be zero.");
        } else {
            return new RequestCreator(this, (Uri)null, resourceId);
        }
    }
    //獲取一個RequestCreator對象
   RequestCreator(Picasso picasso, Uri uri, int resourceId) {
        if(picasso.shutdown) {
          throw new IllegalStateException("Picasso instance already shut down. Cannot  submit new requests.");
        } else {
            this.picasso = picasso;
            this.data = new Builder(uri, resourceId, picasso.defaultBitmapConfig);
        }
    }

接着看,拿到RequestCreator我們在調用into()

 public void into(ImageView target) {
        this.into(target, (Callback)null);
    }



//////////

 public void into(ImageView target, Callback callback) {
       //...
            //前面對請求參數進行校驗,這裏
            Request request1 = this.createRequest(started);
            String requestKey1 = Utils.createKey(request1);
            //查看內存或磁盤中是否有這個圖片
            if(MemoryPolicy.shouldReadFromMemoryCache(this.memoryPolicy)) {
                Bitmap action = this.picasso.quickMemoryCacheCheck(requestKey1);
                //當有緩存的時候
                if(action != null) {
                    this.picasso.cancelRequest(target);
                    PicassoDrawable.setBitmap(target, this.picasso.context, action, LoadedFrom.MEMORY, this.noFade, this.picasso.indicatorsEnabled);
                    if(this.picasso.loggingEnabled) {
                        Utils.log("Main", "completed", request1.plainId(), "from " + LoadedFrom.MEMORY);
                    }

                    if(callback != null) {
                        callback.onSuccess();
                    }

                    return;
                }
            }
            //沒緩存
            ImageViewAction action1 = new ImageViewAction(this.picasso, target, request1, this.memoryPolicy, this.networkPolicy, this.errorResId, this.errorDrawable, requestKey1, this.tag, callback, this.noFade);
            //前面所做的工作就是爲了創建一個ImageViewAction對象
            this.picasso.enqueueAndSubmit(action1);
        }
    }

接着看this.picasso.enqueueAndSubmit(action1);

void enqueueAndSubmit(Action action) {
        Object target = action.getTarget();
        if(target != null && this.targetToAction.get(target) != action) {
            //當將其做個標記
            this.cancelExistingRequest(target);
            this.targetToAction.put(target, action);
        }

        this.submit(action);
    }


void submit(Action action) {
        this.dispatcher.dispatchSubmit(action);
    }
    ///跟蹤到這裏,是不是有點欣慰啊。。哈哈,在構造函數裏面已經對handler有賦值
void dispatchSubmit(Action action) {
        //<font color="red">msg.what=1</font>
        this.handler.sendMessage(this.handler.obtainMessage(1, action));
    }

///如下Dispatcher
    Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler, Downloader downloader, Cache cache, Stats stats) {
        this.dispatcherThread.start();
        Utils.flushStackLocalLeaks(this.dispatcherThread.getLooper());
        this.context = context;
        this.service = service;
        this.hunterMap = new LinkedHashMap();
        this.failedActions = new WeakHashMap();
        this.pausedActions = new WeakHashMap();
        this.pausedTags = new HashSet();
        ////......拿到當前線程的looper,而looper是唯一的,則可以將數據加載到image上
        this.handler = new Dispatcher.DispatcherHandler(this.dispatcherThread.getLooper(), this);
        ////......
        this.downloader = downloader;
        this.mainThreadHandler = mainThreadHandler;
        this.cache = cache;
        this.stats = stats;
        this.batch = new ArrayList(4);
        this.airplaneMode = Utils.isAirplaneModeOn(this.context);
        this.scansNetworkChanges = Utils.hasPermission(context, "android.permission.ACCESS_NETWORK_STATE");
        this.receiver = new Dispatcher.NetworkBroadcastReceiver(this);
        this.receiver.register();
    }

接着往下走,拿到了hanlder之後 msg.what=1; 繼續跟蹤代碼

private static class DispatcherHandler extends Handler {
        private final Dispatcher dispatcher;

        public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
            super(looper);
            this.dispatcher = dispatcher;
        }

        public void handleMessage(final Message msg) {
            Object info;
            BitmapHunter info2;
            Action info3;
            switch(msg.what) {
            case 1:
                info3 = (Action)msg.obj;
                ////.......看這裏..
                this.dispatcher.performSubmit(info3);
                /////.........
                break;
            case 2:
                info3 = (Action)msg.obj;
                this.dispatcher.performCancel(info3);
                break;
            //...
            case 12:
                info = msg.obj;
                this.dispatcher.performResumeTag(info);
            }

        }
    }

繼續go~~!!!!!

void performSubmit(Action action, boolean dismissFailed) {
        if(this.pausedTags.contains(action.getTag())) {
            this.pausedActions.put(action.getTarget(), action);
            if(action.getPicasso().loggingEnabled) {
                Utils.log("Dispatcher", "paused", action.request.logId(), "because tag \'" + action.getTag() + "\' is paused");
            }

        } else {
            ///看這裏,看這裏,,當己錄表有這個的action key
            BitmapHunter hunter = (BitmapHunter)this.hunterMap.get(action.getKey());
            if(hunter != null) {
                hunter.attach(action);
            } else if(this.service.isShutdown()) {
                if(action.getPicasso().loggingEnabled) {
                    Utils.log("Dispatcher", "ignored", action.request.logId(), "because shut down");
                }

            } else {
                //完全第一次加載進來,××× 這裏也是最終的高地
                hunter = BitmapHunter.forRequest(action.getPicasso(), this, this.cache, this.stats, action);
                hunter.future = this.service.submit(hunter);
                this.hunterMap.put(action.getKey(), hunter);
                if(dismissFailed) {
                    this.failedActions.remove(action.getTarget());
                }

                if(action.getPicasso().loggingEnabled) {
                    Utils.log("Dispatcher", "enqueued", action.request.logId());
                }

            }
        }
    }

//////上面函數中最主要的  BitmapHunter  其實就是一個runnable  負責網絡加載圖片
hunter = BitmapHunter.forRequest(action.getPicasso(), this, this.cache, this.stats, action);

///

總結

雖然我們在用Picasso的時候十分簡單,就一行代碼。但是源碼的實現的過程,做爲一名開發者還是十分有必要的仔細閱讀的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章