1.xml 文件定义 animation-list
2.java 文件设置 AnimationDrawable
# [缺点]
- 系统会把每一帧图片读取到内存中
- 当图片不少且每张都很大的状况下,容易出现卡顿,甚至 OOM
复制代码
解决问题的关键在于避免一次性读取全部图片java
[方案] 在每一帧绘制以前,才加载图片到内存中,而且释放前一帧图片的资源
复制代码
inPreferredConfig
设置颜色模式,不带透明度的 RGB_565 内存只有默认的 ARGB_8888 的一半inSampleSize
根据显示控件的大小对图像采样,返回较小的图像以节省内存经过以上处理,能够实现帧动画的流畅播放android
经过 Android Profiler,看到频繁的 IO 操做(每读取一张图片的同时释放一张图片)致使内存剧烈抖动。内存频繁的分配和回收容易产生内存碎片,存在 OOM 的风险,频繁的 GC 也容易致使UI卡顿。缓存
inMutable
设置解码获得的 bitmap 可变inBitmap
复用前一帧图片,避免内存抖动(效果以下图)暂时处理了内存问题后继续思考,频繁的 IO 也会致使 CPU 的使用率高bash
在某华为荣耀9手机上,测试简单页面播放 sd 卡里某一帧动画循环播放的 CPU 状况ide
App 中循环或屡次播放的帧动画大部分状况是局部的小图(什么地方须要无限播放全屏的帧动画呢?),对这类小图添加缓存就挺合适的。优化效果以下图: 测试
另外: 什么业务场景须要帧动画无限循环播放呢?用户会盯着手机上的某个动画多长时间?是否能够针对大部分状况,设置一个上限,播放 n 次以后就中止动画,只保留最后一帧的画面优化
Android 提供了 LruCache,根据最近最少使用优先清理的原则缓存数据。动画
public class FrameAnimationCache extends LruCache<String, Bitmap> {
private static int mCacheSize = (int) (Runtime.getRuntime().maxMemory() / 8);
public FrameAnimationCache() {
super(mCacheSize);
}
@Override
protected int sizeOf(@NonNull String key, @NonNull Bitmap value) {
return value.getByteCount();
}
}
复制代码
对于内存使用不太紧张的 App, 这样一个缓存就够用了,图片缓存最多只会占用 mCacheSize 大小的内存。ui
当缓存里的帧动画图片长时间没有使用,如何释放?this
SoftReference(软引用)若是内存空间足够,垃圾回收器就不会回收它,若是内存空间不足,就会回收这些对象的内存(系统自动帮你回收,不用操心多好)
public class FrameAnimationCache extends LruCache<String, SoftReference<Bitmap>> {
private static int mCacheSize = (int) (Runtime.getRuntime().maxMemory() / 8);
public FrameAnimationCache() {
super(mCacheSize);
}
@Override
protected int sizeOf(@NonNull String key, @NonNull SoftReference<Bitmap> value) {
if (value.get() != null) {
return value.get().getByteCount();
} else {
return 0;
}
}
}
复制代码
当 GC 自动回收 SoftReference,会致使缓存的 sizeOf 计算出错,日志里可能看到这样的警告
W/System.err: java.lang.IllegalStateException: xxx.xxxAnimationCache.sizeOf() is reporting inconsistent results!
W/System.err: at android.support.v4.util.LruCache.trimToSize(LruCache.java:167)
W/System.err: at android.support.v4.util.LruCache.put(LruCache.java:150)
复制代码
假如咱们经过 get(K key) 获取的以前已缓存过的 Bitmap 软引用,而刚好它已被 GC 回收,那么返回 null,须要从新解码图片,调用 put(K key, V value) 缓存起来。
public final V put(@NonNull K key, @NonNull V value) {
if (key != null && value != null) {
Object previous;
synchronized(this) {
++this.putCount;
this.size += this.safeSizeOf(key, value);
previous = this.map.put(key, value);
if (previous != null) {
this.size -= this.safeSizeOf(key, previous);
}
}
if (previous != null) {
this.entryRemoved(false, key, previous, value);
}
this.trimToSize(this.maxSize);
return previous;
} else {
throw new NullPointerException("key == null || value == null");
}
}
复制代码
public void trimToSize(int maxSize) {
while(true) {
Object key;
Object value;
synchronized(this) {
if (this.size < 0 || this.map.isEmpty() && this.size != 0) {
throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
if (this.size <= maxSize || this.map.isEmpty()) {
return;
}
Entry<K, V> toEvict = (Entry)this.map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
this.map.remove(key);
this.size -= this.safeSizeOf(key, value);
++this.evictionCount;
}
this.entryRemoved(true, key, value, (Object)null);
}
}
复制代码
问题已知 —— 数据回收致使大小计算出错,那么解决这个问题就能够了。
ReferenceQueue
而我用了以下方式
public class FrameAnimationCache extends LruCache<String, SizeSoftReferenceBitmap> {
private static int mCacheSize = (int) (Runtime.getRuntime().maxMemory() / 8);
public FrameAnimationCache() {
super(mCacheSize);
}
@Override
protected int sizeOf(@NonNull String key, @NonNull SizeSoftReferenceBitmap value) {
return value.getSize();
}
}
private class SizeSoftReferenceBitmap {
private SoftReference<Bitmap> mBitmap;
private int mSize;
private SizeSoftReferenceBitmap(SoftReference<Bitmap> bitmap, int size) {
mBitmap = bitmap;
mSize = size;
}
private int getSize() {
return mSize;
}
private SoftReference<Bitmap> getBitmap() {
return mBitmap;
}
}
public Bitmap getBitmapFromCache(String key) {
SizeSoftReferenceBitmap value = mFrameAnimationCache.get(key);
return value != null && value.getBitmap() != null ? value.getBitmap().get() : null;
}
public void addBitmapToCache(String key, Bitmap value) {
mFrameAnimationCache.put(key, new SizeSoftReferenceBitmap(new SoftReference<>(value), value.getByteCount()));
}
复制代码
用一个 SizeSoftReferenceBitmap 类,作了简单的对象组合,在建立缓存的时候提早存下 size。