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

Glide munching on memory #1560

Closed
j-garin opened this issue Nov 1, 2016 · 17 comments
Closed

Glide munching on memory #1560

j-garin opened this issue Nov 1, 2016 · 17 comments
Labels

Comments

@j-garin
Copy link

j-garin commented Nov 1, 2016

Hello.

I am using Glide version compile 'com.github.bumptech.glide:glide:3.7.0'
with no integration libraries and bumped into OutOfMemoryException on a Nexus Player running 7.0 when trying to load an image into an ImageView with rounded corners ('com.joooonho:selectableroundedimageview:1.0.1') which extends ImageView.
Memory allocation tracker shows the following:
glide
My code:

		String mediaEndpoint = mediaItem.getMediaEndpoint(url);
		GlideUrl glideUrl = new GlideUrl(mediaEndpoint, headers);
		Glide.with(context)
				.load(glideUrl)
				.asBitmap()
				.signature(new StringSignature(url))
				.placeholder(PlaceholderDrawable.getInstance().getDrawable())
				.centerCrop()
				.diskCacheStrategy(DiskCacheStrategy.SOURCE).into(ivImage);

I need the 'asBitmap()' to implement custom ImageDecoder later on.
Am i missing something?

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

Sorry, my mistake. It was my placeholder drawable

@j-garin j-garin closed this as completed Nov 1, 2016
@TWiStErRob
Copy link
Collaborator

TWiStErRob commented Nov 1, 2016

Yep, that's what I was about the write :)

Tip: With signature you're not replacing the cache key, but adding to it, so it will be mediaEndpoint + url, if you want to replace the key (i.e. not include mediaEndpoint), you'll need to subclass GlideUrl and override some methods, take a look at #607 for an example.

Tip: to make efficient use of Glide, you should consider using a CircleCropTransformation and not a custom view. It's more effcient in a way that Glide can cache the circled image on disk and reload it on scroll. See https://github.com/bumptech/glide#compatibility for links.

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

With Dropbox's APIv2 it's a bit more complicated than endpoint + url. url is unique for each file so can it serve as an identifier?

I need only 2 corners rounded. Is there a Transformation for that?
When the images are encrypted I was recommended to use:

...
        .asBitmap()
        .signature(new StringSignature(url))
                .imageDecoder(decryptingDecoder)
                .cacheDecoder(new FileToStreamDecoder<>(decryptingDecoder))
        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
...

Will the transformation still help in this case?

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

As for the placeholder I had a 1024*1024 drawable. Doesn't Glide shrink it to match ImageViews? Do I need a smaller placeholder or there is a way to reduce its size to display?

@TWiStErRob
Copy link
Collaborator

TWiStErRob commented Nov 1, 2016

Glide doesn't touch the placeholder just calls iv.setImageDrawable at appropriate times (maybe never), so your singleton approach should work fine. It is your fancy view that started re-rasterizing it to a Bitmap. I suggest you create a smaller placeholder, or look into the .thumbnail(Glide.....load(R.drawable.X).fitCenter()) API to have Glide resize it for you.

Yes, glide-transformations linked from Compatibility contains a RoundedCornersTransformation, or you can very easily roll your own. Regarding encryption, decoder is responsible for decoding the steam into a Bitmap, transformation is responsible for making the Bitmap look like it needs to look. See wasabeef/glide-transformations#16 and wasabeef/glide-transformations#32 for combining centerCrop() and custom tranformations.

Regarding DropBox: I assume you have something like http://dropbox.com/foo/1234 as URL and then you get the end point and it becomes http://cdn.dropbox.com/bar/1234. What I meant is Glide already uses whatever you passed in to load() as the cache key. If you add signature as well, the cache key will literally look like:

http://dropbox.com/foo/1234http://cdn.dropbox.com/bar/1234

I was guessing this is not what you were looking for., but instead you want to load http://cdn.dropbox.com/bar/1234, but cache it as http://dropbox.com/foo/1234. This is to prevent re-downloads even if the CDN server or API changes. If this is not what you were aiming for, then sorry for the confusion.

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

Caching is much better with this approach. Thank you.

However, thumbnails/placeholders are not displayed while the images are being loaded. It seems I am missing something. Please give me some hints

Here's my code:

Glide.with(context)
                .load(glideUrl)
                .asBitmap()
                .thumbnail(Glide.with(context).load(R.drawable.placeholder).asBitmap().centerCrop())
                .signature(new StringSignature(url))
                .centerCrop()
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)

@TWiStErRob
Copy link
Collaborator

Hmm, no idea on that yet, maybe your main load is faster than the thumbnail so there's no need to display? Thumbnail will only show if it finishes before the main load, and there will be a small time when nothing is shown (while it's being read from cache), after the first load, memory cache should be used so display is immeditate.

@TWiStErRob
Copy link
Collaborator

Play around with LoggingListener and LoggingTarget to see what's going on.

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

yes, after the cache is filled, it's immediate. but after cleaning the cache it takes about 2-3 seconds to display the images. and in that time thumbnails/placeholders are not shown.
will try to play with those so see what exactly is happening. thank you

@j-garin
Copy link
Author

j-garin commented Nov 1, 2016

Here are the logs. Not sure what exactly they are saying

V/LoggingTarget: [email protected]()

V/LoggingTarget: [email protected](com.bumptech.glide.request.ThumbnailRequestCoordinator@5c7c33e)

V/LoggingTarget: [email protected]()

V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@4f97f9)

V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@4f97f9, 240, 384)

V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@d512ec0)

V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@d512ec0, 240, 384)

V/LoggingTarget: [email protected](null)

V/GLIDE: CardPresenter.onResourceReady(Bitmap@f07ef7f(240x384@ARGB_8888), /d0681722be49237810b44981234aea22/poster.jpg
                      LazyHeaders{headers={User-Agent=[StringHeaderFactory{value='Dalvik/2.1.0 (Linux; U; Android 7.0; Nexus Player Build/NRD91D)'}], Accept-Encoding=[StringHeaderFactory{value='identity'}], Authorization=[StringHeaderFactory{value='<reemoved for security reasons>'}]}}, Target for: ImageView{c93db4c V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}(params=240x384->size=240x384) in Utils.LoggingTarget@37f359f, async, first)

V/LoggingTarget: [email protected](android.graphics.Bitmap@f07ef7f, com.bumptech.glide.request.animation.NoAnimation@8db795)

@TWiStErRob
Copy link
Collaborator

TWiStErRob commented Nov 2, 2016

The two getSize/onSizeReady pairs look like main load and thumbnail load starting up. onLoadStart(null) is because you have no placeholder, this means that view is empty from here on.
async means that the first resource that arrived into the view was loaded from disk cache or over network and first means there was no thumbnail displayed before that Bitmap arrived to the target. Looks like you may have also used dontAnimate().

Further: Did you add LoggingListener to both requests (within thumbnail and outside it)? You removed timing info so I can't help with your 2-3 second delay.

Can you please redo this with both listeners added and timestamps? And also enable internal logging. Also include your Glide current version of the load line to avoid confusion.

@j-garin
Copy link
Author

j-garin commented Nov 7, 2016

Sorry for long pause.

You are right, I am not using animation because i need .asBitmap(). Animations don't work with that.
Here are the updated log with timestamps and the code.

        String mediaEndpoint = mediaItem.getMediaEndpoint(url);
        GlideUrl glideUrl = new KeyGlideUrl(mediaEndpoint, headers, url);
                Glide.with(context)
                .load(glideUrl)
                .asBitmap()
                .thumbnail(Glide.with(context).load(R.drawable.placeholder).asBitmap().listener(new LoggingListener<Integer, Bitmap>()).centerCrop())
                .signature(new StringSignature(url))
                .centerCrop()
                .diskCacheStrategy(DiskCacheStrategy.SOURCE);
                .listener(new LoggingListener<GlideUrl, Bitmap>("CardPresenter"))
                .into(new LoggingTarget<>(new BitmapImageViewTarget(cardView.getMainImageView())))
                .into(cardView.getMainImageView());

11-07 17:52:57.747 V/LoggingTarget: [email protected]()
11-07 17:52:57.748 V/LoggingTarget: [email protected](com.bumptech.glide.request.ThumbnailRequestCoordinator@37f359f)
11-07 17:52:57.748 V/LoggingTarget: [email protected]()
11-07 17:52:57.748 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@5c7c33e)
11-07 17:52:57.748 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@5c7c33e, 240, 384)
11-07 17:52:57.749 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@4f97f9)
11-07 17:52:57.749 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@4f97f9, 240, 384)
11-07 17:52:57.749 V/LoggingTarget: [email protected](null)
11-07 17:52:57.755 V/GLIDE: Thumbnail.onException(null, 2130837727, Utils.LoggingTarget@77a76ec, first)
11-07 17:53:07.405 V/GLIDE: CardPresenter.onResourceReady(Bitmap@4b6049b(240x384@ARGB_8888), /d0681722be49237810b44981234aea22/poster.jpg
                            LazyHeaders{headers={User-Agent=[StringHeaderFactory{value='Dalvik/2.1.0 (Linux; U; Android 7.0; Nexus Player Build/NRD91D)'}], Accept-Encoding=[StringHeaderFactory{value='identity'}], Authorization=[StringHeaderFactory{value='Bearer ZvgI8hEtlYMAAAAAAAAuJFD1bbCpXVbcXZDQ4XXrSevOquBMeks-HoIExGfzr3pL'}]}}, Target for: ImageView{1c96038 V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}(params=240x384->size=240x384) in Utils.LoggingTarget@77a76ec, async, first)
11-07 17:53:07.405 V/LoggingTarget: [email protected](android.graphics.Bitmap@4b6049b, com.bumptech.glide.request.animation.NoAnimation@15f3811)

@TWiStErRob
Copy link
Collaborator

Wow, 10 seconds... we need to see what's Glide doing between those two lines as I said above: https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling#more-logging

Btw, I implemented animations for .asBitmap(), see #1096 for details and #840 for workarounds pre-3.8.0.

@j-garin
Copy link
Author

j-garin commented Nov 8, 2016

Thank you. I will wait for 3.8 release as those animations are not that nescessary for now.
Here are the logs you asked for:

11-08 17:53:00.332 V/LoggingTarget: [email protected]()
11-08 17:53:00.333 V/LoggingTarget: [email protected](com.bumptech.glide.request.ThumbnailRequestCoordinator@4f97f9)
11-08 17:53:00.333 V/LoggingTarget: [email protected]()
11-08 17:53:00.333 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@d512ec0)
11-08 17:53:00.333 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@d512ec0, 240, 384)
11-08 17:53:00.335 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@c295843)
11-08 17:53:00.335 V/LoggingTarget: [email protected](com.bumptech.glide.request.GenericRequest@c295843, 240, 384)
11-08 17:53:00.335 V/LoggingTarget: [email protected](null)
11-08 17:53:00.337 D/skia: --- SkAndroidCodec::NewFromStream returned null
11-08 17:53:00.337 D/skia: --- SkAndroidCodec::NewFromStream returned null
11-08 17:53:00.346 V/GLIDE: Thumbnail.onException(null, 2130837727, Target for: ImageView{80e15d8 V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}, first)
11-08 17:53:00.347 V/GLIDE: Thumbnail.onException(null, 2130837727, Target for: ImageView{5b74d31 V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}, first)
11-08 17:53:00.347 V/GLIDE: Thumbnail.onException(null, 2130837727, Target for: ImageView{d76fc16 V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}, first)
11-08 17:53:00.348 V/GLIDE: Thumbnail.onException(null, 2130837727, Utils.LoggingTarget@5c7c33e, first)
11-08 17:53:00.348 V/GLIDE: Thumbnail.onException(null, 2130837727, Target for: ImageView{b7c3997 V.ED..... ........ 0,0-240,384 #7f1000aa app:id/main_image}, first)
11-08 17:53:03.373 V/GLIDE: CardPresenter.onException(java.net.SocketTimeoutException: timeout, /d0681722be49237810b44981234aea22/poster.jpg
                            LazyHeaders{headers={User-Agent=[StringHeaderFactory{value='Dalvik/2.1.0 (Linux; U; Android 7.0; Nexus Player Build/NRD91D)'}], Accept-Encoding=[StringHeaderFactory{value='identity'}], Authorization=[StringHeaderFactory{value='Bearer ZvgI8hEtlYMAAAAAAAAuJFD1bbCpXVbcXZDQ4XXrSevOquBMeks-HoIExGfzr3pL'}]}}, Utils.LoggingTarget@5c7c33e, first)
                            java.net.SocketTimeoutException: timeout
                                at com.android.okhttp.okio.Okio$3.newTimeoutException(Okio.java:212)
                                at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:250)
                                at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:217)
                                at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:306)
                                at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:300)
                                at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196)
                                at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:191)
                                at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
                                at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:906)
                                at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:782)
                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:463)
                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:405)
                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:521)
                                at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
                                at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java)
                                at com.bumptech.glide.load.data.HttpUrlFetcher.loadDataWithRedirects(HttpUrlFetcher.java:76)
                                at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:44)
                                at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:20)
                                at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:70)
                                at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
                                at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
                                at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
                                at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
                                at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
                                at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
                                at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
                                at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                                at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                                at java.lang.Thread.run(Thread.java:761)
                                at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)
11-08 17:53:03.373 V/LoggingTarget: [email protected](java.net.SocketTimeoutException: timeout, null)
                                    java.net.SocketTimeoutException: timeout
                                        at com.android.okhttp.okio.Okio$3.newTimeoutException(Okio.java:212)
                                        at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:250)
                                        at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:217)
                                        at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:306)
                                        at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:300)
                                        at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196)
                                        at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:191)
                                        at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
                                        at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:906)
                                        at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:782)
                                        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:463)
                                        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:405)
                                        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:521)
                                        at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
                                        at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java)
                                        at com.bumptech.glide.load.data.HttpUrlFetcher.loadDataWithRedirects(HttpUrlFetcher.java:76)
                                        at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:44)
                                        at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:20)
                                        at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:70)
                                        at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
                                        at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
                                        at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
                                        at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
                                        at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
                                        at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
                                        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
                                        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                                        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                                        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                                        at java.lang.Thread.run(Thread.java:761)
                                        at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)

@TWiStErRob
Copy link
Collaborator

That looks like a slow server, Glide cannot boost your connection, nor upgrade your hardware ;)

@j-garin
Copy link
Author

j-garin commented Nov 8, 2016

Having slow load speed is ok. But thumbnails/placeholders are a problem.

@TWiStErRob
Copy link
Collaborator

Not sure what's going on with your placeholder drawable, it's likely not a bitmap: #350
Just use .placeholder(R.dr....) or through your factoring in your opening comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants