GithubHelp home page GithubHelp logo

Comments (24)

Wavesonics avatar Wavesonics commented on August 28, 2024

BitmapDrawable keeps all (or most?) of it's state in a "State" object, that opens up some interesting opportunities:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/graphics/java/android/graphics/drawable/BitmapDrawable.java

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

Also, maybe it's not the book keeping stuff, but the frame buffers that are causing the GC thrashing? When exactly are they allocated?

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

I've confirmed that GifDrawables are definitely not safe to share in their current form. No crashes or anything, but subsequent ImageViews do not animate properly.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Nothing is allocated at Java level during animation in GifDrawable.
I have not checked it yet but I guess that in ListView items, all Views (including its Drawables) which are not visible may be garbage-collected if there is no other references to them. I have no idea now if there is any other way to prevent GC thon keeping references.
Could you provide same sample project where you are experiencing GC thrashing?

I have also not tested single GifDrawable instance with multiple ImageViews. What do you mean by proper animation?

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

So it's not the animation that's the problem, it's any incidental allocations during instantiation.

In ListView's the recycled views are generally not GCed during normal operation. They are given back to you to be reused. It's at this point, if I create a new instance of GifDrawable to set on the recycled ImageView, I get lots of GC activity, where as doing the same with a BitmapDrawable does not.

I will grab the location tracker data and post it.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

Note that I'm still using 1.0.4

OK, so this is just doing a new GifDrawable( byteArray ) each time we need to recycle an ImageView.

The two largest and most frequent allocations are the following:

32416B  int[]   1   pl.droidsonroids.gif.GifDrawable    <init>  
  at pl.droidsonroids.gif.GifDrawable.<init>(GifDrawable.java:133)  

I think that was this line:

mColors = new int[ mMetaData[ 0 ] * mMetaData[ 1 ] ];

These are much smaller, but still quite frequent:

272B    byte[]  1   pl.droidsonroids.gif.GifDrawable    openStream  
  at pl.droidsonroids.gif.GifDrawable.openStream(Native Method) 
  at pl.droidsonroids.gif.GifDrawable.<init>(GifDrawable.java:132)  

The first one seems most significant.

The both occurred ~225 times. Probably once per GifDrawable. Nothing else in the allocation report is anywhere near the size nor frequency of these two.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

Maybe if we could keep around a GifDrawable, but be able to swap out it's backing data, and just reset it's book keeping vars. That way no further allocation would be required, just some code to reset things for the new backing data. idk if this is the cleanest solution though.

edit: on second thought, this isn't the best solution. Really just trying to find a way to cut down on the allocations is the best if we can find a way.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

What about creating something like pool. Pseudo-java-code below (without thread safety, exception handling etc.):

    static class GifDrawablePool
    {
        private final LruCache <Object, GifDrawable> mMap=<LruCache or some map>
        public GifDrawable acquireFromResource(Resources res, int resId)
        {
            GifDrawable drw=mMap.get( resId );
            if (drw!=null)
                return drw;
            drw=new GifDrawable( res, resId );
            mMap.put( resId, drw );
            return drw;
        }
        public GifDrawable acquireFromPath (String path)
               {...
...

mMap will cache some number of recent GifDrawable instances and its size will be controlled to prevent OutOfMemoryErrors. The best keys are immutable objects.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

hhmmm, I think with a sufficient number of unique entries in a data set, this is going to not yield any real gains. Maybe if there was some way of re-using them for different GIFs? But I guess the hard part is that the frame sizes will be different?

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

We can reuse only frame buffers (mColors arrays) because their allocation is most expensive. Given array can be used not only for equal-sized drawables but also for smaller ones. So we can create pool for int arrays which will work as written below (pseudocode):

in GifDrawable constructors:
   int len=mMetaData[ 0 ] * mMetaData[ 1 ];
   mColors=getArrayFromPool(len);//if array having required length or greater is available, return it with removing from pool, return null otherwise
   if (mColors==null)
     mColors = new int[ len ];

when GifDrawable instance is no longer needed:
   addToPool(mColors)

The moment when instance is no longer needed could be eg. onDetachedFromWindow() in GifImageView (finalize() is not guaranteed to be called). In such case we just remember reference to frame buffer for reuse and recycle rest of the GifDrawable.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

Ya this could be a good solution. Maybe as a ctor argument we could accept a pool as an option. Many use cases wouldn't require this optimization, but have it be an optional parameter or something.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

Just found this:
http://developer.android.com/training/displaying-bitmaps/manage-memory.html

Their overall solution is extremely robust, and also extremely intricate.

But the tl;dr is: since HC they added a way to provide a Bitmap which the decoder can re-use, and just decode right into. No allocations required.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

We do not use Bitmap but int array as a frame buffer. Nevertheless it is good idea to use set of SoftReferences as cache container as in their example.
One little additional thing: is most efficient to reuse as small frame buffer as possible so cache may be sorted by size ascending.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Besides "main" frame buffer (int array) there is one another for color indexes at native level (having same dimensions but holding chars instead of ints). If at least one frame uses "restore to previous" disposal method additional buffer is required.
So taking it all together it will be better to reuse all the drawable as you said before. To achieve this we need to add GifDrawable#setInput() method set (with arguments like in corresponding ctros) which will reset internal state, close old backing data input (if needed), open the new input using already allocated memory for buffers (optionally reallocating some parts if needed).
In such case pool will be completely separate from GifDrawable.

from android-gif-drawable.

parizene avatar parizene commented on August 28, 2024

Hi, is it possible to get each frame argb data? Now I'm doing smth like this:

public void getFrameData(int frameIndex, int[] data) {
    seekTo(mGifInfoPtr, frameIndex, data);
    renderFrame(data, mGifInfoPtr, mMetaData);
}

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

@parizene your post is off-topic here. I've added separate issue for your question: #34

from android-gif-drawable.

dougnukem avatar dougnukem commented on August 28, 2024

I'm also running into an issue when trying to reuse GifDrawable's using a Volley backed network cache, and then trying to use that same GifDrawable in 2 ImageViews. When both ImageView's are visible in a ListView one or both of the gif animations are frozen (I'm assuming this is caused by having 2 ImageView's mess with a shared GifDrawable book keeping).

Instead I think I'll have to just cache the byte[] of the gif and create a new GifDrawable for each ImageView.

from android-gif-drawable.

dougnukem avatar dougnukem commented on August 28, 2024

Is there a way to clone a GifDrawable so that we reuse the same underlying allocated data, and just have the GifDrawable responsible for drawing to one ImageView. I've tried allocating a new GifDrawable from a cached byte[] but it makes scrolling through the ListView slower and I'm worried of any leaked memory that might occur.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

@dougnukem one GifDrawable associated with more than one view is not supported now. I'll have to add this information to Readme.
Only way to clone GifDrawable is to construct new one in the same way, exactly as you said. I can add clone() method but it will work also similarly.
Constructing new GifDrawable is heavy and time-consuming process since frame buffer (or buffers) must be allocated. One of the ways to improve this process is to reuse instances of GifDrawable like it was described in previous posts in this issue.
I am planning to do this next week.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Changes from #57 should improve the performance because decoding has been moved to background thread. Code available at experimental branch and in maven snapshot repository.

from android-gif-drawable.

Wavesonics avatar Wavesonics commented on August 28, 2024

The frame decode performance is not the bottle neck. It's the fact that even if you share the actual GIF data, every instance of GifDrawable must allocate it's own frame buffer, and this will cause excessive GC.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Reusing of frame buffer added (in experimental branch), only for that one allocated at Java level by now. Just pass an GifDrawable instance to be reused as an extra argument for constructor. If old drawable is large enough its frame buffer will be reused and rest of it recycled.
If old drawable is larger than constructed one then extra buffer space is also available for next reusing.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Introducing this feature to dev branch started. Instead bunch of constructors there is builder.

from android-gif-drawable.

koral-- avatar koral-- commented on August 28, 2024

Finally done. Feature available on dev branch and maven snapshot repository.
Amount of memory allocated at native level is relatively small in comparison to frame buffer (Bitmap). Additional buffer is needed only for one particular disposal method which is not used in all GIFs. So only Bitmap is tried to be reused.

from android-gif-drawable.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.