編輯:關於Android編程
picasso是Square公司開源的一個Android圖片下載緩存庫,github地址https://github.com/square/picasso,可以實現圖片下載和緩存功能。
Picassso的特點有:
自動將圖像緩存在本地,自帶內存和硬盤二級緩存功能
通過圖片壓縮轉換以減少內存消耗
自動處理了ImageView的回收,自動取消不在視野范圍內的ImageView視圖資源的加載
支持網絡圖片,drawable資源,asset資源,本地圖片等多種資源加載
支持調試,調用函數 Picasso.setIndicatorsEnabled(true) 可以在加載的圖片左上角顯示一個三角形,,不同的顏色代表不同的加載來源。
compile 'com.squareup.picasso:picasso:2.5.2'
只需要一行代碼就能完全實現圖片的異步加載:
Picasso.with(context).load("http://img.my.csdn.net/uploads/201605/08/1462674108_9582.jpg").into(imageView);
@Override public void getView(int position, View convertView, ViewGroup parent) { Imageview view = (ImageView) convertView; if (view == null) { view = new ImageView(context); } String url = getItem(position); Picasso.with(context).load(url).into(view); }
Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(imageView);
picasso提供了兩種占位圖片,未加載完成或者加載發生錯誤的時需要一張圖片作為提示。
Picasso.with(context) .load(url) .placeholder(R.drawable.placeholder) .error(R.drawable.placeholder_error) .into(imageView);
除了加載網絡圖片picasso還支持加載Resources, assets, files, content providers中的資源文件。
Picasso.with(context).load(R.drawable.landing_screen).into(imageview); Picasso.with(context).load(new File(...)).into(imageview); Picasso.with(context).load("file:///android_asset/robert.png").into(imageview);
Picasso使用Builder創建對象,我們一般使用public static Picasso with(Context context)方法
public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }Picasso的with方法返回Picasso的單例,但是有Builderg構造器,Picasso不是嚴格意義上的單例模式。
使用build構建可以自定義線程池、緩存、下載器等方法。
實現RequestHandler接口就可以定義資源加載方式,默認有7種
allRequestHandlers.add(new ResourceRequestHandler(context));//drawable資源圖 allRequestHandlers.add(new ContactsPhotoRequestHandler(context));//通訊錄圖片 allRequestHandlers.add(new MediaStoreRequestHandler(context));//多麼媒體資源庫圖片 allRequestHandlers.add(new ContentStreamRequestHandler(context));//Provider圖片 allRequestHandlers.add(new AssetRequestHandler(context));//asset中的圖片 allRequestHandlers.add(new FileRequestHandler(context));//存儲設備中的圖片 allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));//網絡圖片可以通過extraRequestHandlers添加新的支持方法,定義新的RequestHandler,需要實現兩個方法
public abstract boolean canHandleRequest(Request data); public abstract Result load(Request request, int networkPolicy) throws IOException;canHandleRequest定義了在什麼情況下用這種方式
class NetworkRequestHandler extends RequestHandler { static final int RETRY_COUNT = 2;//重試次數 private static final String SCHEME_HTTP = "http";//識別的scheme private static final String SCHEME_HTTPS = "https";//識別的scheme private final Downloader downloader;//下載器 private final Stats stats;//狀態 /** * 構造方法 */ public NetworkRequestHandler(Downloader downloader, Stats stats) { this.downloader = downloader; this.stats = stats; } @Override public boolean canHandleRequest(Request data) { String scheme = data.uri.getScheme(); return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)); } @Override public Result load(Request request, int networkPolicy) throws IOException { Response response = downloader.load(request.uri, request.networkPolicy); if (response == null) { return null; } Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK; Bitmap bitmap = response.getBitmap(); if (bitmap != null) { return new Result(bitmap, loadedFrom); } InputStream is = response.getInputStream(); if (is == null) { return null; } // Sometimes response content length is zero when requests are being replayed. Haven't found // root cause to this but retrying the request seems safe to do so. if (loadedFrom == DISK && response.getContentLength() == 0) { Utils.closeQuietly(is); throw new ContentLengthException("Received response with 0 content-length header."); } if (loadedFrom == NETWORK && response.getContentLength() > 0) { stats.dispatchDownloadFinished(response.getContentLength()); } return new Result(is, loadedFrom); } @Override int getRetryCount() { return RETRY_COUNT; } @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) { return info == null || info.isConnected(); } @Override boolean supportsReplay() { return true; } static class ContentLengthException extends IOException { public ContentLengthException(String message) { super(message); } } }
Picasso的load方法返回RequestCreator,RequestCreator有兩個功能
配置加載參數。
包括placeHolder與error圖片,加載圖片的大小、旋轉、居中等屬性。
執行加載。
通過調用into(object)方法進行加載。
public RequestCreator load(String path) { if (path == null) { return new RequestCreator(this, null, 0); } if (path.trim().length() == 0) { throw new IllegalArgumentException("Path must not be empty."); } return load(Uri.parse(path)); }into方法只能在主線程調用,否則會拋出異常。如果沒有要加載的資源,請求會被取消,提高了執行效率
static void checkMain() { if (!isMain()) { throw new IllegalStateException("Method call should happen from the main thread."); } }取消操作是Picasso的方法,也只能在主線程調用
/** * Cancel any existing requests for the specified target {@link ImageView}. */ public void cancelRequest(ImageView view) { // checkMain() is called from cancelExistingRequest() if (view == null) { throw new IllegalArgumentException("view cannot be null."); } cancelExistingRequest(view); }
Action是一個抽象類,可以理解為一個加載任務,
FetchAction 緩存圖片
RemoteViewsAction 給RemoteView設置圖片
GetAction 同步獲取圖片
ImageViewAction 給ImageView設置圖片
TargetAction 對Target設置圖片
調用RequestCreator對應方法會對應創建所需要的Action
public void into(Target target) public void fetch() public Bitmap get() throws IOException public void into(ImageView target) public void into(RemoteViews remoteViews, int viewId, int notificationId,Notification notification)
需要特別注意的是 public Bitmap get() throws IOException 是同步方法,其他幾個是異步方法
異步方法將Action提交到Dispatcher,同步方法直接使用BitmapHunter獲取圖片
獲取完圖片,處理圖片的方法是
abstract void complete(Bitmap result, Picasso.LoadedFrom from);不同子類對應不同實現
默認的Dispatcher創建方法
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
cache是圖片緩存,stats是執行狀態
Dispatcher是分發器,由Picasso或Hunter來調用。Dispatcher主要方法有
dispatcherSubmit()和dispatcherCancel()
hunter中加入action便調用dispatcherSubmit(),hunter中取消action便調用dispatcherCancel()
dispatcherComplete()和dispatcherError()
加載結束時調用。均調用batch方法,不過complete操作會將bitmap加入到cache中,以便後續調用。
batch()
起緩沖作用,每隔200毫秒執行一次performBatchComplete()批處理。批處理將hunterList回調給Picasso,Picasso對每個hunter的每個action進行結果回調。
Dispatcher啟動了自己的線程dispatcherThread。DispatcherHandler運行在DispatcherThread中。
RequestCreator中調用了Picasso的submit方法,將acton提交到Dispatcher,
DispatcherHandler發送了REQUEST_SUBMIT這個消息,然後在DispatcherHandler所在線程中執行了performSubmit,
在performSubmit中創建BitmapHunter,進行下載。
void performSubmit(Action action, boolean dismissFailed) { if (pausedTags.contains(action.getTag())) { pausedActions.put(action.getTarget(), action); if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(), "because tag '" + action.getTag() + "' is paused"); } return; } BitmapHunter hunter = hunterMap.get(action.getKey()); if (hunter != null) { hunter.attach(action); return; } if (service.isShutdown()) { if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down"); } return; } hunter = forRequest(action.getPicasso(), this, cache, stats, action); hunter.future = service.submit(hunter); hunterMap.put(action.getKey(), hunter); if (dismissFailed) { failedActions.remove(action.getTarget()); } if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId()); } }
BitmapHunter是一個Runnable,作用是獲取圖片。
BitmapHunter的執行流程:在run()方法中執行hunt()方法嘗試獲取圖片,把結果交給Dispatcher回調。
Bitmap hunt() throws IOException { Bitmap bitmap = null; if (shouldReadFromMemoryCache(memoryPolicy)) { bitmap = cache.get(key); if (bitmap != null) { stats.dispatchCacheHit(); loadedFrom = MEMORY; if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache"); } return bitmap; } } data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy; RequestHandler.Result result = requestHandler.load(data, networkPolicy); if (result != null) { loadedFrom = result.getLoadedFrom(); exifOrientation = result.getExifOrientation(); bitmap = result.getBitmap(); ........ } if (bitmap != null) { if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_DECODED, data.logId()); } stats.dispatchBitmapDecoded(bitmap); if (data.needsTransformation() || exifOrientation != 0) { synchronized (DECODE_LOCK) { if (data.needsMatrixTransform() || exifOrientation != 0) { bitmap = transformResult(data, bitmap, exifOrientation); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId()); } } if (data.hasCustomTransformations()) { bitmap = applyCustomTransformations(data.transformations, bitmap); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations"); } } } if (bitmap != null) { stats.dispatchBitmapTransformed(bitmap); } } } return bitmap; }
內存緩存使用LruCache,實現是在LruCache.java中,動態分配緩存大小,大小為可用內存的1/7,
磁盤緩存使用了網絡框架的緩存方案。
定義了三種下載器,分別用於okhttp3,okhttp,urlconneciton
static Downloader createDefaultDownloader(Context context) { if (SDK_INT >= GINGERBREAD) { try { Class.forName("okhttp3.OkHttpClient"); return OkHttp3DownloaderCreator.create(context); } catch (ClassNotFoundException ignored) { } try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpDownloaderCreator.create(context); } catch (ClassNotFoundException ignored) { } } return new UrlConnectionDownloader(context); }
PicassoExecutorService根據網絡狀況,使用不同的線程池,命中次數多的,優先級高
switch (info.getType()) { case ConnectivityManager.TYPE_WIFI: case ConnectivityManager.TYPE_WIMAX: case ConnectivityManager.TYPE_ETHERNET: setThreadCount(4); break; case ConnectivityManager.TYPE_MOBILE: switch (info.getSubtype()) { case TelephonyManager.NETWORK_TYPE_LTE: // 4G case TelephonyManager.NETWORK_TYPE_HSPAP: case TelephonyManager.NETWORK_TYPE_EHRPD: setThreadCount(3); break; case TelephonyManager.NETWORK_TYPE_UMTS: // 3G case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_EVDO_B: setThreadCount(2); break; case TelephonyManager.NETWORK_TYPE_GPRS: // 2G case TelephonyManager.NETWORK_TYPE_EDGE: setThreadCount(1); break; default: setThreadCount(DEFAULT_THREAD_COUNT); } break; default: setThreadCount(DEFAULT_THREAD_COUNT); }
RecycleView出來已經有一兩個年頭了最近在項目中完全替換掉了ListView很有必要的寫一篇記錄一下使用過程,以便以後溫故而知新。RecycleView的使用場景
最近由於項目的需要,自定義了一個具有側滑功能的listview,側滑後可以點擊編輯、刪除。好了,大家先看一下效果圖,畢竟是看臉的世界。 好了,我要先講一下思路,
前言: 目前工作負責兩個醫療APP項目的開發,同時使用LeanCloud進行雲端配合開發,完全單挑。 現大框架已經完成,正在進行細節模塊上的開發 抽空總結一下And
這是個不錯的教程,自己學完了之後就拿出來分享了,本來想一個帖子寫完,但是發現這樣對自己寫博客的效率有點出入,為了讓大家看的舒服點,所以分開來寫,我們先開看下百度壁紙的客戶