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

Rinsing the Brush – Picasso 3.0 (Droidcon NYC 2018)

Rinsing the Brush – Picasso 3.0 (Droidcon NYC 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 how Picasso works
* Explore ImageDecoder, BitmapFactory's successor in Android P
* 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!

Video: https://youtu.be/VpNwLG3lxbo

Jake Wharton

August 27, 2018
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

  1. 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); }
  2. 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); }
  3. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, (imageDecoder, imageInfo, src) -> { if (r.hasSize()) { imageDecoder.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  4. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, (imageDecoder, imageInfo, src) -> { if (r.hasSize()) { imageDecoder.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  5. Bitmap decodeStreamP(Request r, BufferedSource bs) { ImageDecoder.Source imageSource = ImageDecoder.createSource(ByteBuffer.wrap(bs.readByteArray()));

    return ImageDecoder.decodeBitmap( imageSource, (imageDecoder, imageInfo, src) -> { if (r.hasSize()) { imageDecoder.setTargetSize(r.targetWidth, r.targetHeight); } } }); }
  6. 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) }
  7. Picasso fun loadImage( awsAssetUri: Uri, height: Int, width: Int )

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

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

  10. /** * Sets the stable key for this request to

    be used instead of the * URI or resource ID when caching. * Two requests with the same value are considered to be for the * same resource. */ public RequestCreator stableKey(String stableKey) { data.stableKey(stableKey); return this; }
  11. createNewPicasso(context, 1024, new CustomRequestHandler()); Picasso createNewPicasso( Context context, int cacheSize,

    RequestHandler requestHandler ) {e return new Picasso.Builder(context) e.withCacheSize(cacheSize) e.addRequestHandler(requestHandler) e.build(); }e
  12. createChildPicasso(builder, 1024, new CustomRequestHandler()); Picasso createChildPicasso( Picasso.Builder builder, int cacheSize,

    RequestHandler requestHandler ) { return builder .withCacheSize(cacheSize) .addRequestHandler(requestHandler) .build(); }e
  13. createChildPicasso(picasso, 1024, new CustomRequestHandler()); Picasso createChildPicasso( Picasso picasso, int cacheSize,

    RequestHandler requestHandler ) { return picasso.newBuilder() .withCacheSize(cacheSize) .addRequestHandler(requestHandler) .build(); }e builder Picasso.Builder builder, builder
  14. createChildPicasso(picasso, 1024, new CustomRequestHandler()); Picasso createChildPicasso( Picasso picasso, int cacheSize,

    RequestHandler requestHandler ) { return picasso.newBuilder() .withCacheSize(cacheSize) .addRequestHandler(requestHandler) .build(); }
  15. @Component( modules = RootModule.class ) @RootScope interface RootComponent { @Root

    Picasso picasso(); } @Module class RootModule { @Provides @RootScope @Root Picasso providePicasso(@App Context context) { return new Picasso.Builder(context) .withCacheSize(1024*1024) .build(); } }
  16. @Component( modules = RootModule.class ) @RootScope interface RootComponent { @Root

    Picasso picasso(); } @Module class RootModule { @Provides @RootScope @Root Picasso providePicasso(@App Context context) { return new Picasso.Builder(context) .withCacheSize(1024*1024) .build(); } }
  17. @Component( modules = RootModule.class ) @RootScope interface RootComponent { @Root

    Picasso picasso(); } @Module class RootModule { @Provides @RootScope @Root Picasso providePicasso(@App Context context) { return new Picasso.Builder(context) .withCacheSize(1024*1024) .build(); } }
  18. @Component( dependencies = RootComponent.class, modules = ScreenModule.class ) @ScreenScope interface

    ScreenComponent { } @Module class ScreenModule { @Provides @ScreenScope @Screen Picasso providePicasso(@Root Picasso picasso) { return picasso.newBuilder() .withCacheSize(1024) .build(); } }
  19. @Component( dependencies = RootComponent.class, modules = ScreenModule.class ) @ScreenScope interface

    ScreenComponent { } @Module class ScreenModule { @Provides @ScreenScope @Screen Picasso providePicasso(@Root Picasso picasso) { return picasso.newBuilder() .withCacheSize(1024) .build(); } }
  20. @Component( dependencies = RootComponent.class, modules = ScreenModule.class ) @ScreenScope interface

    ScreenComponent { } @Module class ScreenModule { @Provides @ScreenScope @Screen Picasso providePicasso(@Root Picasso picasso) { return picasso.newBuilder() .withCacheSize(1024) .build(); } }
  21. @Component( dependencies = RootComponent.class, modules = ScreenModule.class ) @ScreenScope interface

    ScreenComponent { } @Module class ScreenModule { @Provides @ScreenScope @Screen Picasso providePicasso(@Root Picasso picasso) { return picasso.newBuilder() .withCacheSize(1024) .build(); } }
  22. /** * Image downloading, transformation, and caching manager. * *

    Use {@see PicassoProvider#get()} for a global singleton * instance or construct your own instance with {@link Builder}. */ public class Picasso implements LifecycleObserver { }
  23. /** * Image downloading, transformation, and caching manager. * *

    Use {@see PicassoProvider#get()} for a global singleton * instance or construct your own instance with {@link Builder}. */ public class Picasso implements LifecycleObserver { }
  24. class SampleActivity extends FragmentActivity { protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState); … getLifecycle().addObserver(picasso); }
  25. class SampleActivity extends FragmentActivity { protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState); … getLifecycle().addObserver(picasso); }
  26. @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void cancelAll() { List<Action> actions = new ArrayList<>(targetToAction.values()); for

    (int i = 0, n = actions.size(); i < n; i++) { cancelExistingRequest(actions.get(i).getTarget()); } List<DeferredRequestCreator> deferredRequestCreators = new ArrayList<>(targetToDeferredRequestCreator.values()); for (int i = 0, n = deferredCreators.size(); i < n; i++) { deferredCreators.get(i).cancel(); } }
  27. public static Picasso with(Context context) { if (singleton == null)

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

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

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

    (singleton != null) { throw new IllegalStateException( "Singleton instance already exists."); } singleton = picasso; } }
  31. @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())); } }
  32. @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())); } }
  33. 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; } }
  34. 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; } }
  35. 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; } }
  36. public final class PicassoContentProvider extends ContentProvider { static Context context;

    @Override public boolean onCreate() { context = getContext(); return true; } … }
  37. interface ImageDecoder { class Image { @Nullable Bitmap bitmap; @Nullable

    Drawable drawable; int exifOrientation; } boolean canHandleSource(BufferedSource source); Image decodeImage(BufferedSource source, Request request) throws IOException; }
  38. class ImageDecoderFactory { List<ImageDecoder> decoders; ImageDecoder getImageDecoderForSource(BufferedSource source) { for

    (ImageDecoder decoder : decoders) { if (decoder.canHandleSource(source)) { return decoder; } } return null; } }
  39. 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)
  40. 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
  41. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  42. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  43. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  44. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  45. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  46. PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA

    PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA METADATA METADATA METADATA MAGIC
  47. InputStream PIXEL DATA PIXEL DATA PIXEL DATA PIXEL DATA PIXEL

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

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

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

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

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

    METADATA METADATA METADATA PIXEL DATA 1920x1080 BitmapFactory PIXEL DATA PIXEL DATA PIXEL DATA
  53. 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)
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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 ???
  68. 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
  69. 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
  70. 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
  71. 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
  72. BitmapFactory InputStream BufferedSource 2PIXEL DATA2 3PIXEL DATA3 4PIXEL DATA4 5PIXEL

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

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

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

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

    MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream 1920x1080 offset: 341
  77. 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
  78. 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
  79. 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°
  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. BitmapFactory InputStream BufferedSource 1METADATA1 MAGIC offset: 341 BitmapFactory inJustDecodeBounds=true SourceBufferingInputStream

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

    1920x1080 offset: 1024 1PIXEL DATA1 3METADATA3 2METADATA2 341 90° ExifInterface SourceBufferingInputStream
  89. abstract class RequestHandler { abstract boolean canHandleRequest(Request request); abstract void

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

    Target (animated GIFs!) • Improved pipelining Coming Soon
  91. • Better separation of concerns: Image/Target/RenderCall • Propagating Drawables to

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