Android 源码解析: 图片加载库Picasso 1
来源:互联网 发布:.win域名 编辑:程序博客网 时间:2024/06/05 13:35
Android 源码解析: 图片加载库Picasso 1
Picasso是一个轻量级的图片缓存库。 Picasso不仅实现了图片异步加载的功能,还解决了android中加载图片时需要解决的一些常见问题:
1.在adapter中需要取消已经不在视野范围的ImageView图片资源的加载,否则会导致图片错位,Picasso已经解决了这个问题。
2.使用复杂的图片压缩转换来尽可能的减少内存消耗
3.自带内存和硬盘二级缓存功能
Picasso有如下特性:
- 处理Adapter中的
ImageView
回收和取消已经回收ImageView的下载进程 - 使用最少的内存完成复杂的图片转换,比如把下载的图片转换为圆角等
- 自动添加磁盘和内存缓存
和其他一些下载图片项目的主要区别之一是:使用4.0+系统上的HTTP缓存来代替磁盘缓存。
同类比较
picasso 和Volley都是用http响应的内容做缓存, 这样可以检测该内容是否过期,如果过期则重新请求新数据,和浏览器缓存一样的策略。
但是在实际应用中我发现大部分的图片缓存都不会过期,也就是一个地址对应一个图片(大部分情况下都是这样 不存在图片过期的问题),并且有些情况应用还有保存图片到本地的功能。
所以综合对比情况如下:
1. picasso 比 AUIL(Android-Universal-Image-Loader) 的代码简单写
2. picasso 具有检测缓存是否失效并重新获取功能
3. AUIL具有保存图片或者二次利用图片的性能优势,直接拿过来就可以用了 不用再次解析http响应数据。
使用:
Picasso使用的方法汇总:Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);Picasso.with(context).load(url).into(view);Picasso.with(context).load(url) .resize(50, 50).centerCrop().into(imageView);//这里的placeholder将resource传入通过getResource.getDrawable取资源,所以可以是张图片也可以是color idPicasso.with(context).load(url).placeholder(R.drawable.user_placeholder).error(R.drawable.user_placeholder_error).into(imageView);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);//这里显示notification的图片Picasso.with(activity).load(Data.URLS[new Random().nextInt(Data.URLS.length)]).resizeDimen(R.dimen.notification_icon_width_height, R.dimen.notification_icon_width_height).into(remoteViews, R.id.photo, NOTIFICATION_ID, notification);//这里是通过设置tag标签,就是当前传过来的context,这样就可以根据这个context tag来pause和resume显示了Picasso.with(context).load(url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().tag(context).into(view);//监听onScrollStateChanged的时候调用执行picasso.resumeTag(context);picasso.pauseTag(context);Picasso.with(context).load(contactUri).placeholder(R.drawable.contact_picture_placeholder).tag(context).into(holder.icon);//这个onpause方法里的这段代码还是很有意思的@Override protected void onPause() { super.onPause(); if (isFinishing()) { // Always cancel the request here, this is safe to call even if the image has been loaded. // This ensures that the anonymous callback we have does not prevent the activity from // being garbage collected. It also prevents our callback from getting invoked even after the // activity has finished. Picasso.with(this).cancelRequest(imageView); } }// Trigger the download of the URL asynchronously into the image view. Picasso.with(context) .load(url) .placeholder(R.drawable.placeholder) .error(R.drawable.error) .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size) .centerInside() .tag(context) .into(holder.image);//Picasso.with使用的是单例模式Picasso.with(this).cancelTag(this);
如上,Picasso 的使用是非常简单的:
Picasso.with(context).load(
"http://i.imgur.com/DvpvklR.png"
).into(imageView);
/** * The global default {@link Picasso} instance. * <p> * This instance is automatically initialized with defaults that are suitable * to most implementations. * <ul> * <li>LRU memory cache of 15% the available application RAM</li> * <li>Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: * this is only available on API 14+ <em>or</em> if you are using a standalone * library that provides a disk cache on all API levels like OkHttp)</li> * <li>Three download threads for disk and network access.</li> * </ul> * <p> * If these settings do not meet the requirements of your application you can * construct your own with full control over the configuration by using * {@link Picasso.Builder} to create a {@link Picasso} instance. You can * either use this directly or by setting it as the global instance with * {@link #setSingletonInstance}. */ public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }
- 一个典型的单例模式
- 使用的是延时初始化来保证Picasso只有在使用到的时候,才会初始化对象
- 使用同步代码块的办法来保证多个线程中同时请求的时候,能够保证只创建唯一的单例对象。
- 同步对象为类对象,保证同步范围整个JVM中。
- 使用构造者模式,通过Builder类构建了一个默认配置的Picasso的对象。
/** Create the {@link Picasso} instance. */ public Picasso build() { Context context = this.context; if (downloader == null) { <span style="color:#ff0000;">downloader = Utils.createDefaultDownloader(context);</span> } if (cache == null) { <span style="color:#ff0000;">cache = new LruCache(context);</span> } if (service == null) { <span style="color:#ff0000;">service = new PicassoExecutorService();</span> } if (transformer == null) { <span style="color:#ff0000;">transformer = RequestTransformer.IDENTITY;</span> } <span style="color:#ff0000;"> Stats stats = new Stats(cache); Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);</span> <span style="color:#ff6666;"> return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats, defaultBitmapConfig, indicatorsEnabled, loggingEnabled);</span> } }
- Downloader
- DownLoader就是下载用的工具类,在Picasso当中,如果OKHttp可以使用的话,就会默认使用OKHttp,如果无法使用的话,就会使用UrlConnectionDownloader(默认使用HttpURLConnection实现)。
- Cache
- 默认实现为LruCache,就是使用LinkedHashMap实现的一个Cache类,注意的一个地方就是,在其他的地方,我们一般默认的是限制的capacity,但是这个地方我们是限制的总共使用的内存空间。因此LruCache在实现的时候,其实简单理解就是将LinkedHashMap封装,然后基于LinkedHashMap的方法实现Cache的方法,在Cache的set()方法的时候,会不断计算当前还可以使用的空间大小,要是超出范围,则删除之前保存的数据。
- ExecutorService
- 默认的实现为PicassoExecutorService,该类也比较简单,其实就是ThreadPoolExecutor,在其功能的基础上继续封装,在其中有一个比较细心的功能就是,Picasso通过PicassoExecutorService设置线程数量,来调整在2G/3G/4G/WiFi不同网络情况下的不同表现。
- RequestTransformer
- ReqeustTransformer是一个接口,用来预处理Reqeust,可以用来将请求进行预先处理,比如改个域名啥的。
- Stats
- 主要是一些统计信息,比如cache hit/miss,总共下载的文件大小,下载过的图片数量,转换的图片数量等等。
- Dispatcher
- Picasso当中,分发任务的线程,这是我们以后要重点研究的一个类,先标记一下,这个Dispatcher主要做了以下的事情:
- 启动了一个DispatcherThread线程
- 初始化了一个用来处理消息的DispatcherHandler,注意,根据Dispatcher中默认配置,该Handler所有数据的处理是在DispatcherThread之上。
- 初始化并注册了一个网络状态广播接收器。
- Picasso当中,分发任务的线程,这是我们以后要重点研究的一个类,先标记一下,这个Dispatcher主要做了以下的事情:
/** * Start an image request using the specified URI. * <p> * Passing {@code null} as a {@code uri} will not trigger any request but will * set a placeholder, if one is specified. * * @see #load(File) * @see #load(String) * @see #load(int) */ public RequestCreator load(Uri uri) { return new <span style="color:#ff0000;">RequestCreator</span>(this, uri, 0); } /** * Start an image request using the specified path. This is a convenience * method for calling {@link #load(Uri)}. * <p> * This path may be a remote URL, file resource (prefixed with {@code file:}), * content resource (prefixed with {@code content:}), or android resource * (prefixed with {@code android.resource:}. * <p> * Passing {@code null} as a {@code path} will not trigger any request but * will set a placeholder, if one is specified. * * @see #load(Uri) * @see #load(File) * @see #load(int) * @throws IllegalArgumentException * if {@code path} is empty or blank string. */ 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)); } /** * Start an image request using the specified image file. This is a * convenience method for calling {@link #load(Uri)}. * <p> * Passing {@code null} as a {@code file} will not trigger any request but * will set a placeholder, if one is specified. * <p> * Equivalent to calling {@link #load(Uri) load(Uri.fromFile(file))}. * * @see #load(Uri) * @see #load(String) * @see #load(int) */ public RequestCreator load(File file) { if (file == null) { return new RequestCreator(this, null, 0); } return load(Uri.fromFile(file)); } /** * Start an image request using the specified drawable resource ID. * * @see #load(Uri) * @see #load(String) * @see #load(File) */ public RequestCreator load(int resourceId) { if (resourceId == 0) { throw new IllegalArgumentException("Resource ID must not be zero."); } return new RequestCreator(this, null, resourceId); }Picasso通过调用load(Uri uri),返回了一个传入Picasso对象和要访问的网址或路径的Uri的ReqeustCreator。
RequestCreator(Picasso picasso, Uri uri, int resourceId) { if (picasso.shutdown) { throw new IllegalStateException( "Picasso instance already shut down. Cannot submit new requests."); } this.picasso = picasso; data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig); }
/** * Asynchronously fulfills the request into the specified {@link ImageView}. * <p> * <em>Note:</em> This method keeps a weak reference to the {@link ImageView} * instance and will automatically support object recycling. */ public void into(ImageView target) { into(target, null); }into(ImageView imageview)实际上调用的是into(ImageView imageview, Callback callback)方法,我们查看其源代码:
/** * Asynchronously fulfills the request into the specified {@link ImageView} * and invokes the target {@link Callback} if it's not {@code null}. * <p> * <em>Note:</em> The {@link Callback} param is a strong reference and will * prevent your {@link android.app.Activity} or {@link android.app.Fragment} * from being garbage collected. If you use this method, it is <b>strongly</b> * recommended you invoke an adjacent * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent * temporary leaking. */ public void into(ImageView target, Callback callback) { long started = System.nanoTime(); checkMain(); if (target == null) { throw new IllegalArgumentException("Target must not be null."); } if (!data.hasImage()) { picasso.cancelRequest(target); if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } return; } if (deferred) { if (data.hasSize()) { throw new IllegalStateException("Fit cannot be used with resize."); } int width = target.getWidth(); int height = target.getHeight(); if (width == 0 || height == 0) { if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } picasso.defer(target, new DeferredRequestCreator(this, target, callback)); return; } data.resize(width, height); } Request request = createRequest(started); String requestKey = createKey(request); if (shouldReadFromMemoryCache(memoryPolicy)) { Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); if (bitmap != null) { picasso.cancelRequest(target); setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled, request.isShowGif); if (picasso.loggingEnabled) { log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY); } if (callback != null) { callback.onSuccess(); } return; } } if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action); }
- 首先检查当前是否工作在主线程,如果不是在主线程上的话,直接抛出异常退出。
- 然后判断data.hasImage(),这里面这个函数命名比较迷惑人了,一开始我还以为是判断是否已经有解析好的图片,但再往下看一看源代码,原来这个代码是判断Uri和resourceId是否有设置了一个,如果没有,则说明该Reqeust没有数据源,是错误的。
- 如果没有Uri或者resource Id,那么则取消该请求,并设置默认的显示图片并退出。
- 该图片是否需要延时执行(这个属性还不太清楚在哪里配置的,不清楚是手动指定还是自动设置)。如果需要延时执行,则按照以下步骤执行。
- 判断是否设置过targetSize,如果已经设置过,则抛出异常退出 (不太明白为什么要检查是否已经要设置过targetSize)
- 然后获取target,即ImageView对应的长和宽,如果长和宽都是0,那么就设置默认的图片,并构建一个DeferredRequestCreator,放入Picasso对应的队列当中。
- 重新设置data即ReqeustCreator对应的targetWidth和targetHeight
- 创建Reqeust,生成requestKey
- 判断是否需要跳过MemoryCache,如果不跳过,那么就尝试获取图片,并取消对应的请求,进行回调。
- 如果需要设置默认的图片,则在这里进行设置
- 生成对应的ImageViewAction
- 将生成的ImageViewAction添加到队列当中。
into()方法总结:检查配置的合法性,然后根据配置决定是放入延时队列还是立刻执行,如果立刻执行,则创建对应的请求并尝试从MemoryCache中获取,如果不成功,则生成对应的Action并提交到Picasso的队列当中。然后就是Picasso的任务调度了。
- Android 源码解析: 图片加载库Picasso 1
- Android 源码解析: 图片加载库Picasso 2 Cache机制
- Android 源码解析: 图片加载库Picasso 3 核心类
- Android Picasso图片加载库源码剖析
- Picasso图片加载框架源码解析
- 图片加载框架Picasso源码解析
- Android 源码解析: 图片加载库Picasso 4 任务调度 Dispatcher
- Android之图片加载库Picasso源码分析
- Android 图片加载框架Picasso基本使用和源码完全解析
- picasso Android图片加载
- 图片加载利器之Picasso(四)源码解析
- 图片加载框架Picasso解析
- 图片加载利器Picasso 解析
- 图片加载框架Picasso解析
- Android图片加载库:Picasso详解
- Android:图片加载库Glide VS Picasso
- Android图片加载库:Picasso讲解
- Android图片加载库Picasso和Glide
- Xcode7创建 .pch 文件
- linux find 命令忽略某个或多个子目录的方法
- 数据结构--图 的JAVA实现(上)
- git
- 495个C语言问题(摘录)
- Android 源码解析: 图片加载库Picasso 1
- 怎样理解阻塞非阻塞与同步异步的区别?
- 第七周 项目3-负数把正数赶出队列
- 初探SocialFramework和UIActivityViewController
- [11]Container With Most Water
- Android 事件分发机制完全解析(续)
- Student Information System
- 黑马程序员———C语言 函数
- 光照模型的计算方式总结(1.Diffuse分量2.Specular分量 Phong model和Blinn-Phong model (Or Blinn))