相信做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;
}
}
...
接下來看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的時候十分簡單,就一行代碼。但是源碼的實現的過程,做爲一名開發者還是十分有必要的仔細閱讀的。