Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rinsing the Brush - Picasso 3.0 (Chicago Roboto 2018)

Rinsing the Brush - Picasso 3.0 (Chicago Roboto 2018)

Picasso is a powerful image downloading and caching library for Android but since its launch in 2013, other libraries have improved or entered the scene and new requirements have come up.

In this talk, we’ll:
* Dig into the internals of Picasso works
* Compare and contrast to other image libraries
* Discuss latest improvements as we push to 3.0
…and more!

You’ll learn to efficiently work with different image formats across millions of devices!

Co-Presented with @JakeWharton.

John Rodriguez

April 13, 2018
Tweet

More Decks by John Rodriguez

Other Decks in Programming

Transcript

  1. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  2. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  3. Bitmap decodeStreamPreP(Request r, BufferedSource bs) { BitmapFactory.Options options = createBitmapOptions(request);

    if (requiresInSampleSize(options)) { InputStream stream = new SourceBufferingInputStream(bs); BitmapFactory.decodeStream(stream, null, options); calculateInSampleSize(r.targetWidth, r.targetHeight, options, r); } return BitmapFactory.decodeStream(bs.inputStream(), null, options); }
  4. Bitmap decodeStreamPreP(Request r, BufferedSource bs) { BitmapFactory.Options options = createBitmapOptions(request);

    if (requiresInSampleSize(options)) { InputStream stream = new SourceBufferingInputStream(bs); BitmapFactory.decodeStream(stream, null, options); calculateInSampleSize(r.targetWidth, r.targetHeight, options, r); } return BitmapFactory.decodeStream(bs.inputStream(), null, options); }
  5. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, new OnHeaderDecodedListener() { void onHeaderDecoded(ImageDecoder d, ImageInfo info, Source src) { if (r.hasSize()) { d.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  6. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, new OnHeaderDecodedListener() { void onHeaderDecoded(ImageDecoder d, ImageInfo info, Source src) { if (r.hasSize()) { d.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  7. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, new OnHeaderDecodedListener() { void onHeaderDecoded(ImageDecoder d, ImageInfo info, Source src) { if (r.hasSize()) { d.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  8. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  9. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  10. java.io.IOException: Cannot reset at com.squareup.picasso.MarkableInputStream.reset(MarkableInputStream.java:99) at com.squareup.picasso.BitmapHunter.decodeStream(BitmapHunter.java:140) at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:217) at

    com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:159) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764) at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:411)
  11. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC nightmare_shrek.png
  12. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  13. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  14. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  15. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  16. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  17. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  18. InputStream PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL

    DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  19. BitmapFactory inJustDecodeBounds=true MAGIC METADATA METADATA METADATA PIXEL DATA MarkableInputStream MAGIC

    METADATA METADATA METADATA PIXEL DATA 1920x1080 BitmapFactory MAGIC METADATA METADATA METADATA PIXEL DATA
  20. BitmapFactory inJustDecodeBounds=true MAGIC METADATA METADATA METADATA PIXEL DATA MarkableInputStream MAGIC

    METADATA METADATA METADATA PIXEL DATA 1920x1080 MAGIC METADATA METADATA METADATA PIXEL DATA BitmapFactory
  21. PIXEL DATA PIXEL DATA PIXEL DATA BitmapFactory inJustDecodeBounds=true MarkableInputStream MAGIC

    METADATA METADATA METADATA PIXEL DATA 1920x1080 BitmapFactory
  22. PIXEL DATA PIXEL DATA PIXEL DATA BitmapFactory inJustDecodeBounds=true MarkableInputStream MAGIC

    METADATA METADATA METADATA PIXEL DATA 1920x1080 BitmapFactory PIXEL DATA PIXEL DATA PIXEL DATA
  23. MarkableInputStream BitmapFactory inJustDecodeBounds=true ??? BitmapFactory java.io.IOException: Cannot reset at com.squareup.picasso.MarkableInputStream.reset(MarkableInputStream.java:99)

    at com.squareup.picasso.BitmapHunter.decodeStream(BitmapHunter.java:140) at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:217) at com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:159) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764) at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:411)
  24. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 1024 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 2048
  25. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 2048 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 1024
  26. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 2048 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4
  27. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 2048
  28. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 2048 5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 3072
  29. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3072 5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 2048
  30. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3072 5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7
  31. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3072
  32. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3072
  33. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset:x3072 8PIXEL DATA8 9PIXEL DATA9 3754
  34. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3754 8PIXEL DATA8 9PIXEL DATA9 x3072
  35. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3754 8PIXEL DATA8 9PIXEL DATA9
  36. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3754
  37. BitmapFactory inJustDecodeBounds=true BufferedSource SourceBufferingInputStream 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4

    5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3754 ???
  38. BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL DATA5 6PIXEL

    DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC offset: 3754 ??? BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  39. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  40. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  41. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  42. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  43. BitmapFactory InputStream BufferedSource 5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL

    DATA8 9PIXEL DATA9 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream
  44. ExifInterface BufferedSource SourceBufferingInputStream 1METADATA1 MAGIC 1METADATA1 MAGIC offset: 341 BitmapFactory

    inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 341 1METADATA1 MAGIC
  45. ExifInterface BufferedSource SourceBufferingInputStream 1METADATA1 MAGIC 1METADATA1 MAGIC offset: 341 BitmapFactory

    inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 341 1METADATA1 MAGIC
  46. ExifInterface BufferedSource SourceBufferingInputStream 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 341
  47. ExifInterface BufferedSource SourceBufferingInputStream 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341
  48. ExifInterface BufferedSource SourceBufferingInputStream 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341
  49. ExifInterface BufferedSource SourceBufferingInputStream 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90°
  50. BufferedSource 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1 MAGIC offset:

    341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  51. BitmapFactory InputStream BufferedSource 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  52. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 1PIXEL

    DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  53. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 1PIXEL DATA1 3METADATA3 2METADATA2 1METADATA1 MAGIC 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  54. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

    DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL DATA8 9PIXEL DATA9 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  55. BitmapFactory InputStream BufferedSource 5PIXEL DATA5 6PIXEL DATA6 7PIXEL DATA7 8PIXEL

    DATA8 9PIXEL DATA9 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  56. BitmapFactory InputStream BufferedSource 8PIXEL DATA8 9PIXEL DATA9 1METADATA1 MAGIC offset:

    341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  57. BitmapFactory InputStream BufferedSource 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream

    1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  58. BitmapFactory InputStream BufferedSource 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream

    1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  59. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  60. Picasso fun loadImage( remoteUri: Uri, @ColorInt tintColor: Int? = null

    ) { val creator = picasso.load(remoteUri) .placeholder(avatarPlaceholder) .fit() if (transformTintColor != null) { creator.transform(TintTransformation(tintColor)) } creator.into(this) }
  61. Picasso fun loadImage( awsAssetUri: Uri, height: Int, width: Int )

    { val creator = picasso.load(awsAssetUri) .placeholder(avatarPlaceholder) .fit() .into(this) }
  62. Picasso fun loadImage( awsAssetUri: Uri, height: Int, width: Int )

    { val creator = picasso.load(awsAssetUri) .placeholder(avatarPlaceholder) .fit() .into(this) }
  63. ?

  64. public static Picasso with(Context context) { if (singleton == null)

    { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }
  65. public static Picasso with(Context context) { if (singleton == null)

    { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }
  66. public static void setSingletonInstance(Picasso picasso) { synchronized (Picasso.class) { if

    (singleton != null) { throw new IllegalStateException( "Singleton instance already exists."); } singleton = picasso; } }
  67. public static void setSingletonInstance(Picasso picasso) { synchronized (Picasso.class) { if

    (singleton != null) { throw new IllegalStateException( "Singleton instance already exists."); } singleton = picasso; } }
  68. @Module(includes = ThumborModule.class) final class ProductionPicassoModule { @Provides @Singleton static

    Picasso providePicasso( @App Context context, OkHttpClient client, Thumbor thumbor) { Picasso.RequestTransformer transformer = new PollexorRequestTransformer(thumbor); return new Picasso.Builder(context) .client(client) .requestTransformer(transformer) .listener((picasso, uri, e) -> Timber.d(e, uri.toString())); } }
  69. @Module(includes = ThumborModule.class) final class ProductionPicassoModule { @Provides @Singleton static

    Picasso providePicasso( @App Context context, OkHttpClient client, Thumbor thumbor) { Picasso.RequestTransformer transformer = new PollexorRequestTransformer(thumbor); return new Picasso.Builder(context) .client(client) .requestTransformer(transformer) .listener((picasso, uri, e) -> Timber.d(e, uri.toString())); } }
  70. public final class PicassoProvider { private static volatile Picasso instance;

    public static Picasso get() { if (instance == null) { synchronized (PicassoProvider.class) { if (instance == null) { if (PicassoContentProvider.context == null) { throw new IllegalStateException("context == null"); } instance = new Picasso.Builder(PicassoContentProvider.context).build(); } } } return instance; } }
  71. public final class PicassoProvider { private static volatile Picasso instance;

    public static Picasso get() { if (instance == null) { synchronized (PicassoProvider.class) { if (instance == null) { if (PicassoContentProvider.context == null) { throw new IllegalStateException("context == null"); } instance = new Picasso.Builder(PicassoContentProvider.context).build(); } } } return instance; } }
  72. public final class PicassoProvider { private static volatile Picasso instance;

    public static Picasso get() { if (instance == null) { synchronized (PicassoProvider.class) { if (instance == null) { if (PicassoContentProvider.context == null) { throw new IllegalStateException("context == null"); } instance = new Picasso.Builder(PicassoContentProvider.context).build(); } } } return instance; } }
  73. public final class PicassoContentProvider extends ContentProvider { static Context context;

    @Override public boolean onCreate() { context = getContext(); return true; } … }
  74. • Android P • OkHttp 2.x => 3.x
 • Okio

    integration
 • Improvements 3.x Goals
  75. abstract class RequestHandler { abstract boolean canHandleRequest(Request request); abstract void

    load(Picasso picasso, Request request, Callback callback); }X Result int networkPolicy
  76. • Better separation of concerns: Source/Target/Load • Propagating Drawables to

    Target (animated GIFs!) • More? Let us know! Coming Soon