android加载大量图片内存优化,Android图片加载内存优化

利用BitmapFactory.Options实现图片内存优化

通过设置options.inPreferredConfig控制内存占用

首先准备了一张1280x800的blue_bg.png图片,我们知道这张图片加载到内存默认占用的大小是1280x800x4 = 4096000byte

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg);

// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节

// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());

}

我们知道可以通过设置options.inPreferredConfig,来设置图片加载时候占用的字节ALPHA_8(1byte),RGB_565(2),ARGB_4444(2),ARGB_8888(4)。

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Bitmap.Config.RGB_565;

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());

}

经过日志分析,发现并没有生效,占用的内存并没有减少一半。大概的原因是

设备加载图片时候,不同android版本对图片编解码的支持不一样

图片的格式也决定了他不能支持通过Bitmap.Config.RGB_565模式去加载,所以会选择默认ARGB_8888模式去加载,所以我们的内存占用并没有减少。

这篇文章会有详细的介绍:文章链接

通过设置不同的inPreferredConfig值真的能减少Bitmap加载时占用的内存么?链接

我们增加一张one.jpg图片做对比,具体代码如下:

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Bitmap.Config.RGB_565;

// options.inSampleSize = 2;

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);

// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节

// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());

}

Bitmap one = BitmapFactory.decodeResource(getResources(), R.drawable.one,options);

// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节

// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

Log.d("zyl--","one = " + one.getAllocationByteCount() +", ="+one.getByteCount());

}

通过日志我们发现,jpg图片对于设置inPreferredConfig属性生效了,内存占用减少了一半。进一步证实了图片格式对于inPreferredConfig属性是否生效有一定影响。

通过设置采样率options.inSampleSize来减少图片内存占用

通过设置采样率options.inSampleSize来减少图片内存占用问题。inSampleSize 参数,可以实现 Bitmap 采样压缩,这个参数的含义是宽高维度上每隔 inSampleSize 个像素进行一次采集。

BitmapFactory.Options options = new BitmapFactory.Options();

// options.inPreferredConfig = Bitmap.Config.RGB_565;

options.inSampleSize = 2;

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);

// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节

// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());

}

通过日志分析,得到当采样率为2的时候,图片占用内存为

: bitmap = 1024000,

内存减少了1/4。

通过设置 Options.inBitmap,使Bitmap 对象重复使用,节省内存

实现点击切换图片的功能,代码如下:

public class MainActivity extends AppCompatActivity {

private int resIndex;

int[] resIds = {R.drawable.one, R.drawable.two};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

TextView hello = findViewById(R.id.hello);

final ImageView iv = findViewById(R.id.iv);

hello.setOnClickListener(new View.OnClickListener() {

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

@Override

public void onClick(View v) {

iv.setImageBitmap(getBitmap());

}

});

reuseBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.one);

}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

private Bitmap getBitmap() {

return BitmapFactory.decodeResource(getResources(), resIds[resIndex++ % 2], options);

}

以上代码运行后,发现当我们切换图片时,内存情况如图:

5cf7b7b373d1

内存抖动

每次切换图片都需要通过 BitmapFactory 创建一个新的 Bitmap 对象。当方法执行完毕后,这个 Bitmap 又会被 GC 回收,这就造成不断地创建和销毁比较大的内存对象,从而导致频繁 GC(或者叫内存抖动)。像 Android App 这种面相最终用户交互的产品,如果因为频繁的 GC 造成 UI 界面卡顿,还是会影响到用户体验的。可以在 Android Studio Profiler 中查看内存情况。

使用Options.inBitmap,重复利用已经占用内存的 Bitmap 空间,解决内存抖动问题。

public class MainActivity extends AppCompatActivity {

private int resIndex;

int[] resIds = {R.drawable.one, R.drawable.two};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

TextView hello = findViewById(R.id.hello);

final ImageView iv = findViewById(R.id.iv);

hello.setOnClickListener(new View.OnClickListener() {

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

@Override

public void onClick(View v) {

// Intent intent = new Intent(MainActivity.this,MyActivity.class);

// startActivity(intent);

iv.setImageBitmap(getBitmap());

}

});

BitmapFactory.Options options = new BitmapFactory.Options();

//设置为true,使这块内存能够复用

options.inMutable =true;

reuseBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.one,options);

}

/**

* 重用bitmap

*/

private Bitmap reuseBitmap;

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

private Bitmap getBitmap() {

BitmapFactory.Options options = new BitmapFactory.Options();

options.inSampleSize = 1;

//为true 只解析bitmap的占用内存大小,不加载bitmap到内存中

options.inJustDecodeBounds = true;

BitmapFactory.decodeResource(getResources(), resIds[resIndex % 2], options);

if (canUseForInBitmap(reuseBitmap, options)) {

options.inMutable = true;

options.inBitmap = reuseBitmap;

}

//恢复设置

options.inJustDecodeBounds = false;

return BitmapFactory.decodeResource(getResources(), resIds[resIndex++ % 2], options);

}

static boolean canUseForInBitmap(

Bitmap candidate, BitmapFactory.Options targetOptions) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

// From Android 4.4 (KitKat) onward we can re-use if the byte size of

// the new bitmap is smaller than the reusable bitmap candidate

// allocation byte count.

int width = targetOptions.outWidth / targetOptions.inSampleSize;

int height = targetOptions.outHeight / targetOptions.inSampleSize;

int byteCount = width * height * getBytesPerPixel(candidate.getConfig());

return byteCount <= candidate.getAllocationByteCount();

}

// On earlier versions, the dimensions must match exactly and the inSampleSize must be 1

return candidate.getWidth() == targetOptions.outWidth

&& candidate.getHeight() == targetOptions.outHeight

&& targetOptions.inSampleSize == 1;

}

/**

* A helper function to return the byte usage per pixel of a bitmap based on its configuration.

*/

static int getBytesPerPixel(Bitmap.Config config) {

if (config == Bitmap.Config.ARGB_8888) {

return 4;

} else if (config == Bitmap.Config.RGB_565) {

return 2;

} else if (config == Bitmap.Config.ARGB_4444) {

return 2;

} else if (config == Bitmap.Config.ALPHA_8) {

return 1;

}

return 1;

}

}

运行后发现内存抖动的问题解决了:

5cf7b7b373d1

内存图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值