Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SizeSpec experiment #2287

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ buildscript {
androidxFragment: 'androidx.fragment:fragment:1.4.0',
androidxLifecycle: 'androidx.lifecycle:lifecycle-common:2.4.0',
androidxStartup: 'androidx.startup:startup-runtime:1.1.0',
coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0',
junit: 'junit:junit:4.13.2',
truth: 'com.google.truth:truth:1.1.3',
robolectric: 'org.robolectric:robolectric:4.6.1',
Expand Down
1 change: 1 addition & 0 deletions picasso/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
implementation deps.androidxAnnotations
implementation deps.androidxCore
implementation deps.androidxExifInterface
implementation deps.coroutines

testImplementation deps.junit
testImplementation deps.truth
Expand Down
3 changes: 2 additions & 1 deletion picasso/src/main/java/com/squareup/picasso3/BitmapHunter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import java.util.concurrent.CountDownLatch
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.runBlocking

internal open class BitmapHunter(
val picasso: Picasso,
Expand Down Expand Up @@ -101,7 +102,7 @@ internal open class BitmapHunter(
}

if (retryCount == 0) {
data = data.newBuilder().networkPolicy(NetworkPolicy.OFFLINE).build()
data = runBlocking { data.newBuilder().networkPolicy(NetworkPolicy.OFFLINE).build() }
}

val resultReference = AtomicReference<RequestHandler.Result?>()
Expand Down
9 changes: 6 additions & 3 deletions picasso/src/main/java/com/squareup/picasso3/Dispatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import com.squareup.picasso3.Utils.hasPermission
import com.squareup.picasso3.Utils.isAirplaneModeOn
import com.squareup.picasso3.Utils.log
import java.util.concurrent.ExecutorService
import kotlinx.coroutines.launch

internal class Dispatcher internal constructor(
private val context: Context,
Expand Down Expand Up @@ -335,10 +336,12 @@ internal class Dispatcher internal constructor(
logId = getLogIdsForHunter(hunter)
)
}
if (hunter.exception is ContentLengthException) {
hunter.data = hunter.data.newBuilder().networkPolicy(NO_CACHE).build()
hunter.picasso.scope.launch {
if (hunter.exception is ContentLengthException) {
hunter.data = hunter.data.newBuilder().networkPolicy(NO_CACHE).build()
}
hunter.future = service.submit(hunter)
}
hunter.future = service.submit(hunter)
} else {
performError(hunter)
// Mark for replay only if we observe network info changes and support replay.
Expand Down
11 changes: 10 additions & 1 deletion picasso/src/main/java/com/squareup/picasso3/Picasso.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ import okhttp3.OkHttpClient
import java.io.File
import java.io.IOException
import java.util.concurrent.ExecutorService
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel

/**
* Image downloading, transformation, and caching manager.
Expand Down Expand Up @@ -80,7 +85,8 @@ class Picasso internal constructor(
* **WARNING:** Enabling this will result in excessive object allocation. This should be only
* be used for debugging purposes. Do NOT pass `BuildConfig.DEBUG`.
*/
@Volatile var isLoggingEnabled: Boolean
@Volatile var isLoggingEnabled: Boolean,
coroutineContext: CoroutineContext = Dispatchers.Main.immediate,
) : LifecycleObserver {
@get:JvmName("-requestTransformers")
internal val requestTransformers: List<RequestTransformer> = requestTransformers.toList()
Expand All @@ -101,6 +107,8 @@ class Picasso internal constructor(
@set:JvmName("-shutdown")
internal var shutdown = false

internal val scope = CoroutineScope(SupervisorJob() + coroutineContext)

init {
// Adjust this and Builder(Picasso) as internal handlers are added or removed.
val builtInHandlers = 8
Expand Down Expand Up @@ -361,6 +369,7 @@ class Picasso internal constructor(
return
}
cache.clear()
scope.cancel()

close()

Expand Down
13 changes: 12 additions & 1 deletion picasso/src/main/java/com/squareup/picasso3/Request.kt
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ class Request internal constructor(builder: Builder) {
var stableKey: String? = null
var targetWidth = 0
var targetHeight = 0
var sizeSpec: SizeSpec = SizeSpec.Unspecified
var centerCrop = false
var centerCropGravity = 0
var centerInside = false
Expand Down Expand Up @@ -389,6 +390,10 @@ class Request internal constructor(builder: Builder) {
tag = null
}

fun sizeSpec(spec: SizeSpec) {
sizeSpec = spec
}

/**
* Resize the image to the specified size in pixels.
* Use 0 as desired dimension to resize keeping aspect ratio.
Expand Down Expand Up @@ -555,7 +560,13 @@ class Request internal constructor(builder: Builder) {
}

/** Create the immutable [Request] object. */
fun build(): Request {
suspend fun build(): Request {
val size = sizeSpec.resolve()
if (size is SizeSpec.Size.Exact) {
targetWidth = size.width
targetHeight = size.height
}

check(!(centerInside && centerCrop)) {
"Center crop and center inside can not be used together."
}
Expand Down
178 changes: 94 additions & 84 deletions picasso/src/main/java/com/squareup/picasso3/RequestCreator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import com.squareup.picasso3.Utils.checkNotMain
import com.squareup.picasso3.Utils.log
import java.io.IOException
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

/** Fluent API for building an image download request. */
class RequestCreator internal constructor(
Expand All @@ -58,6 +60,7 @@ class RequestCreator internal constructor(
@DrawableRes private var errorResId = 0
private var placeholderDrawable: Drawable? = null
private var errorDrawable: Drawable? = null
private var sizeSpec: SizeSpec = SizeSpec.Unspecified

/** Internal use only. Used by [DeferredRequestCreator]. */
@get:JvmName("-tag")
Expand Down Expand Up @@ -155,6 +158,7 @@ class RequestCreator internal constructor(
* *Note:* This method works only when your target is an [ImageView].
*/
fun fit(): RequestCreator {
check(sizeSpec == SizeSpec.Unspecified) { "Use only one of fit() or sizeSpec()." }
deferred = true
return this
}
Expand All @@ -173,6 +177,11 @@ class RequestCreator internal constructor(
return this
}

fun sizeSpec(spec: SizeSpec) {
check(!deferred) { "Use only one of fit() or sizeSpec()." }
sizeSpec = spec
}

/**
* Resize the image to the specified dimension size.
* Use 0 as desired dimension to resize keeping aspect ratio.
Expand Down Expand Up @@ -354,7 +363,7 @@ class RequestCreator internal constructor(
return null
}

val request = createRequest(started)
val request = runBlocking { createRequest(started) }
val action = GetAction(picasso, request)
val result =
forRequest(picasso, picasso.dispatcher, picasso.cache, action).hunt() ?: return null
Expand Down Expand Up @@ -387,20 +396,22 @@ class RequestCreator internal constructor(
data.priority(Picasso.Priority.LOW)
}

val request = createRequest(started)
if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
if (picasso.isLoggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + LoadedFrom.MEMORY)
picasso.scope.launch {
val request = createRequest(started)
if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
if (picasso.isLoggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + LoadedFrom.MEMORY)
}
callback?.onSuccess()
return@launch
}
callback?.onSuccess()
return
}
}

val action = FetchAction(picasso, request, callback)
picasso.submit(action)
val action = FetchAction(picasso, request, callback)
picasso.submit(action)
}
}
}

Expand Down Expand Up @@ -437,19 +448,21 @@ class RequestCreator internal constructor(
return
}

val request = createRequest(started)
if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
picasso.cancelRequest(target)
target.onBitmapLoaded(bitmap, LoadedFrom.MEMORY)
return
picasso.scope.launch {
val request = createRequest(started)
if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
picasso.cancelRequest(target)
target.onBitmapLoaded(bitmap, LoadedFrom.MEMORY)
return@launch
}
}
}

target.onPrepareLoad(if (setPlaceholder) getPlaceholderDrawable() else null)
val action = BitmapTargetAction(picasso, target, request, errorDrawable, errorResId)
picasso.enqueueAndSubmit(action)
target.onPrepareLoad(if (setPlaceholder) getPlaceholderDrawable() else null)
val action = BitmapTargetAction(picasso, target, request, errorDrawable, errorResId)
picasso.enqueueAndSubmit(action)
}
}

/**
Expand All @@ -471,18 +484,20 @@ class RequestCreator internal constructor(
"Cannot use placeholder or error drawables with remote views."
}

val request = createRequest(started)
val action = NotificationAction(
picasso,
request,
errorResId,
RemoteViewsTarget(remoteViews, viewId),
notificationId,
notification,
notificationTag,
callback
)
performRemoteViewInto(request, action)
picasso.scope.launch {
val request = createRequest(started)
val action = NotificationAction(
picasso,
request,
errorResId,
RemoteViewsTarget(remoteViews, viewId),
notificationId,
notification,
notificationTag,
callback
)
performRemoteViewInto(request, action)
}
}

/**
Expand Down Expand Up @@ -515,17 +530,19 @@ class RequestCreator internal constructor(
"Cannot use placeholder or error drawables with remote views."
}

val request = createRequest(started)
val action = AppWidgetAction(
picasso,
request,
errorResId,
RemoteViewsTarget(remoteViews, viewId),
appWidgetIds,
callback
)

performRemoteViewInto(request, action)
picasso.scope.launch {
val request = createRequest(started)
val action = AppWidgetAction(
picasso,
request,
errorResId,
RemoteViewsTarget(remoteViews, viewId),
appWidgetIds,
callback
)

performRemoteViewInto(request, action)
}
}

/**
Expand All @@ -552,50 +569,43 @@ class RequestCreator internal constructor(
}

if (deferred) {
check(!data.hasSize()) { "Fit cannot be used with resize." }
val width = target.width
val height = target.height
if (width == 0 || height == 0) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable())
}
picasso.defer(target, DeferredRequestCreator(this, target, callback))
return
}
data.resize(width, height)
sizeSpec = ImageViewSizeSpec(target)
}

val request = createRequest(started)
picasso.scope.launch {
val request = createRequest(started)

if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
picasso.cancelRequest(target)
val result: RequestHandler.Result = RequestHandler.Result.Bitmap(bitmap, LoadedFrom.MEMORY)
setResult(target, picasso.context, result, noFade, picasso.indicatorsEnabled)
if (picasso.isLoggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + LoadedFrom.MEMORY)
if (shouldReadFromMemoryCache(request.memoryPolicy)) {
val bitmap = picasso.quickMemoryCacheCheck(request.key)
if (bitmap != null) {
picasso.cancelRequest(target)
val result: RequestHandler.Result =
RequestHandler.Result.Bitmap(bitmap, LoadedFrom.MEMORY)
setResult(target, picasso.context, result, noFade, picasso.indicatorsEnabled)
if (picasso.isLoggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + LoadedFrom.MEMORY)
}
callback?.onSuccess()
return@launch
}
callback?.onSuccess()
return
}
}

if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable())
}

val action = ImageViewAction(
picasso,
target,
request,
errorDrawable,
errorResId,
noFade,
callback
)
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable())
}

picasso.enqueueAndSubmit(action)
val action = ImageViewAction(
picasso,
target,
request,
errorDrawable,
errorResId,
noFade,
callback
)

picasso.enqueueAndSubmit(action)
}
}

private fun getPlaceholderDrawable(): Drawable? {
Expand All @@ -607,7 +617,7 @@ class RequestCreator internal constructor(
}

/** Create the request optionally passing it through the request transformer. */
private fun createRequest(started: Long): Request {
private suspend fun createRequest(started: Long): Request {
val id = nextId.getAndIncrement()
val request = data.build()
request.id = id
Expand Down
Loading