工具类 解决帧动画内存泄漏问题

来源:互联网 发布:苏氏钻头淘宝 编辑:程序博客网 时间:2024/06/03 18:37

package com.hp.robot.util;

import android.app.Activity;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.widget.ImageView;

import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* 此工具类源于stack over flow
* 原文链接:http://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android
* 主要使用了BitmapFactory.decodeByteArray方法通过底层C来绘制图片,有效防止OOM
* 使用了第三方类库:org.apache.commons.io.IOUtils,将Inputstream转为byte字节数组
* */
public class MyAnimationDrawable {

public static class MyFrame {    byte[] bytes;    int duration;    Drawable drawable;    boolean isReady = false;}public interface OnDrawableLoadedListener {    public void onDrawableLoaded(List<MyFrame> myFrames);}// 1/*** * 性能更优 * 在animation-list中设置时间 * **/public static void animateRawManuallyFromXML(int resourceId,                                             final ImageView imageView, final int soundsId, final Runnable onStart,                                             final Runnable onComplete) {    loadRaw(resourceId, imageView.getContext(),            new OnDrawableLoadedListener() {                @Override                public void onDrawableLoaded(List<MyFrame> myFrames) {                    if (onStart != null) {                        onStart.run();                    }                    //Intent intent = new Intent(imageView.getContext(), MediaPlayerService.class);                    //intent.putExtra("soundId",soundsId);                    //imageView.getContext().startService(intent);                    animateRawManually(myFrames, imageView, onComplete);                }            });}//repeat animpublic static AnimationDrawableController animateRawManuallyFromXMLRepeat(int resourceId,                                                                          final ImageView imageView, final Runnable onStart,                                                                          final AnimationDrawableController controller                                             ) {    loadRaw(resourceId, imageView.getContext(),            new OnDrawableLoadedListener() {                @Override                public void onDrawableLoaded(List<MyFrame> myFrames) {                    if (onStart != null) {                        onStart.run();                    }                    //Intent intent = new Intent(imageView.getContext(), MediaPlayerService.class);                    //intent.putExtra("soundId",soundsId);                    //imageView.getContext().startService(intent);                    animateRawManuallyRepeat(myFrames, imageView,0,controller);                }            });    return controller;}// 2private static void loadRaw(final int resourceId, final Context context,                            final OnDrawableLoadedListener onDrawableLoadedListener) {    loadFromXml(resourceId, context, onDrawableLoadedListener);}// 3private static void loadFromXml(final int resourceId,                                final Context context,                                final OnDrawableLoadedListener onDrawableLoadedListener) {    new Thread(new Runnable() {        @Override        public void run() {            final ArrayList<MyFrame> myFrames = new ArrayList<MyFrame>();            XmlResourceParser parser = context.getResources().getXml(                    resourceId);            try {                int eventType = parser.getEventType();                while (eventType != XmlPullParser.END_DOCUMENT) {                    if (eventType == XmlPullParser.START_DOCUMENT) {                    } else if (eventType == XmlPullParser.START_TAG) {                        if (parser.getName().equals("item")) {                            byte[] bytes = null;                            int duration = 1000;                            for (int i = 0; i < parser.getAttributeCount(); i++) {                                if (parser.getAttributeName(i).equals(                                        "drawable")) {                                    int resId = Integer.parseInt(parser                                            .getAttributeValue(i)                                            .substring(1));                                    bytes = IOUtils.toByteArray(context                                            .getResources()                                            .openRawResource(resId));                                } else if (parser.getAttributeName(i)                                        .equals("duration")) {                                    duration = parser.getAttributeIntValue(                                            i, 1000);                                }                            }                            MyFrame myFrame = new MyFrame();                            myFrame.bytes = bytes;                            myFrame.duration = duration;                            myFrames.add(myFrame);                        }                    } else if (eventType == XmlPullParser.END_TAG) {                    } else if (eventType == XmlPullParser.TEXT) {                    }                    eventType = parser.next();                }            } catch (IOException e) {                e.printStackTrace();            } catch (XmlPullParserException e2) {                // TODO: handle exception                e2.printStackTrace();            }            // Run on UI Thread            new Handler(context.getMainLooper()).post(new Runnable() {                @Override                public void run() {                    if (onDrawableLoadedListener != null) {                        onDrawableLoadedListener.onDrawableLoaded(myFrames);                    }                }            });        }    }).run();}// 4private static void animateRawManually(List<MyFrame> myFrames,                                       ImageView imageView, Runnable onComplete) {    animateRawManually(myFrames, imageView, onComplete, 0);}// 4private static void animateRawManually(List<MyFrame> myFrames, final int soundsId,                                       ImageView imageView, Runnable onComplete) {    animateRawManually(myFrames, soundsId,imageView, onComplete, 0);}// 5private static void animateRawManually(final List<MyFrame> myFrames,                                       final ImageView imageView, final Runnable onComplete,                                       final int frameNumber) {    final MyFrame thisFrame = myFrames.get(frameNumber);    if (frameNumber == 0) {        thisFrame.drawable = new BitmapDrawable(imageView.getContext()                .getResources(), BitmapFactory.decodeByteArray(                thisFrame.bytes, 0, thisFrame.bytes.length));    } else {        MyFrame previousFrame = myFrames.get(frameNumber - 1);        ((BitmapDrawable) previousFrame.drawable).getBitmap().recycle();        previousFrame.drawable = null;        previousFrame.isReady = false;    }    imageView.setImageDrawable(thisFrame.drawable);    new Handler().postDelayed(new Runnable() {        @Override        public void run() {            // Make sure ImageView hasn't been changed to a different Image            // in this time            if (imageView.getDrawable() == thisFrame.drawable) {                if (frameNumber + 1 < myFrames.size()) {                    MyFrame nextFrame = myFrames.get(frameNumber + 1);                    if (nextFrame.isReady) {                        // Animate next frame                        animateRawManually(myFrames, imageView, onComplete,                                frameNumber + 1);                    } else {                        nextFrame.isReady = true;                    }                } else {                    if (onComplete != null) {                        onComplete.run();                    }                }            }        }    }, thisFrame.duration);    // Load next frame    if (frameNumber + 1 < myFrames.size()) {        new Thread(new Runnable() {            @Override            public void run() {                MyFrame nextFrame = myFrames.get(frameNumber + 1);                nextFrame.drawable = new BitmapDrawable(imageView                        .getContext().getResources(),                        BitmapFactory.decodeByteArray(nextFrame.bytes, 0,                                nextFrame.bytes.length));                if (nextFrame.isReady) {                    // Animate next frame                    animateRawManually(myFrames, imageView, onComplete,                            frameNumber + 1);                } else {                    nextFrame.isReady = true;                }            }        }).run();    }}// 5private static AnimationDrawableController animateRawManuallyRepeat(final List<MyFrame> myFrames,                                                                    final ImageView imageView,                                                                    final int frameNumber, final AnimationDrawableController controller) {    if(controller.mStop){        return controller;    }    final MyFrame thisFrame = myFrames.get(frameNumber);    if (frameNumber == 0) {        thisFrame.drawable = new BitmapDrawable(imageView.getContext()                .getResources(), BitmapFactory.decodeByteArray(                thisFrame.bytes, 0, thisFrame.bytes.length));    } else {        MyFrame previousFrame = myFrames.get(frameNumber - 1);        ((BitmapDrawable) previousFrame.drawable).getBitmap().recycle();        previousFrame.drawable = null;        previousFrame.isReady = false;    }    imageView.setImageDrawable(thisFrame.drawable);    new Handler().postDelayed(new Runnable() {        @Override        public void run() {            // Make sure ImageView hasn't been changed to a different Image            // in this time            if (imageView.getDrawable() == thisFrame.drawable) {                if (frameNumber + 1 < myFrames.size()) {                    MyFrame nextFrame = myFrames.get(frameNumber + 1);                    if (nextFrame.isReady) {                        // Animate next frame                        animateRawManuallyRepeat(myFrames, imageView,                                frameNumber + 1,controller);                    } else {                        nextFrame.isReady = true;                    }                } else {                }            }        }    }, thisFrame.duration);    // Load next frame    if (frameNumber + 1 < myFrames.size()) {        new Thread(new Runnable() {            @Override            public void run() {                MyFrame nextFrame = myFrames.get(frameNumber + 1);                nextFrame.drawable = new BitmapDrawable(imageView                        .getContext().getResources(),                        BitmapFactory.decodeByteArray(nextFrame.bytes, 0,                                nextFrame.bytes.length));                if (nextFrame.isReady) {                    // Animate next frame                    animateRawManuallyRepeat(myFrames, imageView,                            frameNumber + 1,controller);                } else {                    nextFrame.isReady = true;                }            }        }).run();    }else {        animateRawManuallyRepeat(myFrames, imageView,                0,controller);    }    return controller;}// 5private static void animateRawManually(final List<MyFrame> myFrames, final int soundsId,                                       final ImageView imageView, final Runnable onComplete,                                       final int frameNumber) {    final MyFrame thisFrame = myFrames.get(frameNumber);    if (frameNumber == 0) {        thisFrame.drawable = new BitmapDrawable(imageView.getContext()                .getResources(), BitmapFactory.decodeByteArray(                thisFrame.bytes, 0, thisFrame.bytes.length));    } else {        MyFrame previousFrame = myFrames.get(frameNumber - 1);        ((BitmapDrawable) previousFrame.drawable).getBitmap().recycle();        previousFrame.drawable = null;        previousFrame.isReady = false;    }    imageView.setImageDrawable(thisFrame.drawable);    new Handler().postDelayed(new Runnable() {        @Override        public void run() {            // Make sure ImageView hasn't been changed to a different Image            // in this time

// MediaPlayer player = MediaPlayer.create(imageView.getContext(), R.raw.crab);
// player.start();
if (imageView.getDrawable() == thisFrame.drawable) {
if (frameNumber + 1 < myFrames.size()) {
MyFrame nextFrame = myFrames.get(frameNumber + 1);

                    if (nextFrame.isReady) {                        // Animate next frame                        animateRawManually(myFrames, imageView, onComplete,                                frameNumber + 1);                    } else {                        nextFrame.isReady = true;                    }                } else {                    ((Activity) imageView.getContext()).runOnUiThread(onComplete);                }

// imageView.getContext().getApplicationContext().run
//
// runOnUiThread(new Runnable()
// {
// public void run()
// {
// mText.setText(“更新界面-runOnUiThread”) ;
// }
//
// });
// }
// if (onComplete != null) {
// onComplete.run();
// }
// }
}
}
}, thisFrame.duration);
// Load next frame
if (frameNumber + 1 < myFrames.size()) {
new Thread(new Runnable() {
@Override
public void run() {
MyFrame nextFrame = myFrames.get(frameNumber + 1);
nextFrame.drawable = new BitmapDrawable(imageView
.getContext().getResources(),
BitmapFactory.decodeByteArray(nextFrame.bytes, 0,
nextFrame.bytes.length));
if (nextFrame.isReady) {
// Animate next frame
animateRawManually(myFrames, imageView, onComplete,
frameNumber + 1);
} else {
nextFrame.isReady = true;
}

            }        }).run();    }}//第二种方法/*** * 代码中控制时间,但不精确 * duration = 1000; * ****/public static void animateManuallyFromRawResource(        int animationDrawableResourceId, ImageView imageView,        Runnable onStart, Runnable onComplete, int duration) throws IOException,        XmlPullParserException {    AnimationDrawable animationDrawable = new AnimationDrawable();    XmlResourceParser parser = imageView.getContext().getResources()            .getXml(animationDrawableResourceId);    int eventType = parser.getEventType();    while (eventType != XmlPullParser.END_DOCUMENT) {        if (eventType == XmlPullParser.START_DOCUMENT) {        } else if (eventType == XmlPullParser.START_TAG) {            if (parser.getName().equals("item")) {                Drawable drawable = null;                for (int i = 0; i < parser.getAttributeCount(); i++) {                    if (parser.getAttributeName(i).equals("drawable")) {                        int resId = Integer.parseInt(parser                                .getAttributeValue(i).substring(1));                        byte[] bytes = IOUtils.toByteArray(imageView                                .getContext().getResources()                                .openRawResource(resId));//IOUtils.readBytes                        drawable = new BitmapDrawable(imageView                                .getContext().getResources(),                                BitmapFactory.decodeByteArray(bytes, 0,                                        bytes.length));                    } else if (parser.getAttributeName(i)                            .equals("duration")) {                        duration = parser.getAttributeIntValue(i, 66);                    }                }                animationDrawable.addFrame(drawable, duration);            }        } else if (eventType == XmlPullParser.END_TAG) {        } else if (eventType == XmlPullParser.TEXT) {        }        eventType = parser.next();    }    if (onStart != null) {        onStart.run();    }    animateDrawableManually(animationDrawable, imageView, onComplete, 0);}private static void animateDrawableManually(        final AnimationDrawable animationDrawable,        final ImageView imageView, final Runnable onComplete,        final int frameNumber) {    final Drawable frame = animationDrawable.getFrame(frameNumber);    imageView.setImageDrawable(frame);    new Handler().postDelayed(new Runnable() {        @Override        public void run() {            // Make sure ImageView hasn't been changed to a different Image            // in this time            if (imageView.getDrawable() == frame) {                if (frameNumber + 1 < animationDrawable.getNumberOfFrames()) {                    // Animate next frame                    animateDrawableManually(animationDrawable, imageView,                            onComplete, frameNumber + 1);                } else {                    // Animation complete                    if (onComplete != null) {                        onComplete.run();                    }                }            }        }    }, animationDrawable.getDuration(frameNumber));}

}

在活动中调用
第一个参数:动画文件
第二个参数:目标view ImagevView
第三个参数:播放动画时候需要播放的音乐资源ID
第四个参数:动画开始时执行的方法Runnable对象
第四个参数:动画结束时执行的方法Runnable对象
MyAnimationDrawable.animateRawManuallyFromXML(R.drawable.robot_hand_anim, mIvRobot, 0, new Runnable() {
@Override
public void run() {
}
},
new Runnable() {
@Override
public void run() {
System.gc();
}
});

动画文件:

原创粉丝点击