android 多图片优化工具,Android APP内存优化之图片优化

APP中的按钮、背景、动画变换基本上全是图片,在2K屏上(分辨率2048*1536)一张背景图片就会占用内存12M,来回切换几次内存占用就会增涨到上百兆,为了在不影响APP的视觉效果的前提下,有必要通过各种手段来降低APP对内存的消耗,下面是我在实践过程中使用的一些方法。

通过DDMS的APP内存占用查看工具分析发现,APP中占用内存最多的是图片,每个Activity中图片占用内存占大半,本文重点分享对图片的内存优化。

不要将Button的背景设置为selector

在布局文件和代码中,都可以为Button设置background为selector,这样方便实现按钮的正反选效果,但实际跟踪发现,如果是将Button的背景设置为selector,在初始化Button的时候会将正反选图片都加载在内存中(具体可以查看Android源码,在类Drawable.java的createFromXmlInner方法中对图片进行解析,最终调用Drawable的inflate方法),相当于一个按钮占用了两张相同大小图片所使用的内存,如果一个界面上按钮很多或者是按钮很大,光是按钮占用的内存就会很大,可以通过在布局文件中给按钮只设置正常状态下的背景图片,然后在代码中监听按钮的点击状态,当按下按钮时为按钮设置反选效果的图片,抬起时重新设置为正常状态下的背景,具体实现方式如下:

public class ImageButtonClickUtils {

private ImageButtonClickUtils(){

}

/**

* 设置按钮的正反选效果

*

* */

public static void setClickState(View view, final int normalResId, final int pressResId){

view.setOnTouchListener(new OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:{

v.setBackgroundResource(pressResId);

}

break;

case MotionEvent.ACTION_MOVE:{

v.setBackgroundResource(pressResId);

}

break;

case MotionEvent.ACTION_UP:{

v.setBackgroundResource(normalResId);

}

break;

default:{

}

break;

}

// 为了不影响监听按钮的onClick回调,返回值应为false

return false;

}

});

}

}

通过上面这种方式就可以解决同一个按钮占用两倍内存的问题,如果你觉得为一个按钮提供正反选两张图片会导致APK的体积变大,可以通过如下方式实现按钮点击的反选效果,这种方式既不会存在Button占用两倍内存的情况,又减小了APK的体积(Android 5.0中的tintColor也可以实现类似的效果):

ImageButton personalInfoBtn = (ImageButton)findViewById(R.id.personalBtnId);

personalInfoBtn.setOnTouchListener(new OnTouchListener() {

@SuppressLint("ClickableViewAccessibility")

@Override

public boolean onTouch(View v, MotionEvent event) {

int action = event.getAction();

if(action == MotionEvent.ACTION_DOWN){

((ImageButton)v).setColorFilter(getResources().getColor(0X50000000));

}else if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL){

((ImageButton)v).clearColorFilter();

}

// 为了不影响监听按钮的onClick回调,返回值应为false

return false;

}

});

尽量少用AnimationDrawable,如果必须要可以自定义图片切换器代替AnimationDrawable

AnimationDrawable也是一个耗内存大户,图片帧数越多耗内存越大,具体可以查看AnimationDrawable的源码,在AnimationDrawable实例化的时候,Drawable的createFromXmlInner方法会调用AnimationDrawable的inflate方法,该方法里面有一个while循环去一次性将所有帧都读取出来,也就是在初始化的时候就将所有的帧读在内存中了,有多少张图片,它就要消耗对应大小的内存。

可以自定义一个ImageView来实现AnimationDrawable的功能,根据图片之间切换的时间间隔来定时设置ImageView的背景图片,这样始终只有一个ImageView实例,更换的只是其背景,占用内存会比AnimationDrawable小很多:

//图片动态切换器

public class AnimImageView {

private static final int MSG_START = 0xf1;

private static final int MSG_STOP = 0xf2;

private static final int STATE_STOP = 0xf3;

private static final int STATE_RUNNING = 0xf4;

//运行状态

private int mState = STATE_RUNNING;

private ImageView mImageView;

//图片资源ID列表

private List mResourceIdList = null;

//定时任务

private Timer mTimer = null;

private AnimTimerTask mTimeTask = null;

//记录播放位置

private int mFrameIndex = 0;

//播放形式

private boolean isLooping = false;

public AnimImageView( ){

mTimer = new Timer();

}

// 设置动画播放资源

public void setAnimation( HanziImageView imageview, List resourceIdList ){

mImageView = imageview;

mResourceIdList = resourceIdList;

}

/**

* 开始播放动画

* @param loop 时候循环播放

* @param duration 动画播放时间间隔

* */

public void start(boolean loop, int duration){

stop();

isLooping = loop;

mFrameIndex = 0;

mState = STATE_RUNNING;

mTimeTask = new AnimTimerTask( );

mTimer.schedule(mTimeTask, 0, duration);

}

/**

* 停止动画播放

*

* */

public void stop(){

if (mTimeTask != null) {

mFrameIndex = 0;

mState = STATE_STOP;

mTimer.purge();

mTimeTask.cancel();

mTimeTask = null;

mImageView.setBackgroundResource(0);

}

}

/**

* 定时器任务

*

*

*/

class AnimTimerTask extends TimerTask {

@Override

public void run() {

if(mFrameIndex < 0 || mState == STATE_STOP){

return;

}

if( mFrameIndex < mResourceIdList.size() ){

Message msg = AnimHanlder.obtainMessage(MSG_START,0,0,null);

msg.sendToTarget();

}else{

mFrameIndex = 0;

if(!isLooping){

Message msg = AnimHanlder.obtainMessage(MSG_STOP,0,0,null);

msg.sendToTarget();

}

}

}

}

private Handler AnimHanlder = new Handler(){

public void handleMessage(android.os.Message msg) {

switch (msg.what) {

case MSG_START:{

if(mFrameIndex >=0 && mFrameIndex < mResourceIdList.size() && mState == STATE_RUNNING){

mImageView.setImageResource(mResourceIdList.get(mFrameIndex));

mFrameIndex++;

}

}

break;

case MSG_STOP:{

if (mTimeTask != null) {

mFrameIndex = 0;

mTimer.purge();

mTimeTask.cancel();

mState = STATE_STOP;

mTimeTask = null;

mImageView.setImageResource(0);

}

}

break;

default:

break;

}

}

};

}

其它优化方式

尽量将Activity中的小图片和背景合并,一张小图片既浪费布局的时间,又平白地增加了内存占用;

对于在需要时才显示的图片或者布局,可以使用ViewStub标签,通过sdk/tools目录下的hierarchyviewer.bat查看布局文件会发现,使用viewstub标签的组件几乎不消耗布局的时间,在代码中当需要显示时再去实例化有助于提高Activity的布局效率和节省Activity消耗的内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值