文章目录
- ImageView资源的利用和优化
- 1.利用Android Studio Profiler 工具解析应用的内存数据
- 内存类型包含:
- 2.分析ImageView操作以及内存的关系
- 2.1.前提条件
- 2.2.测试开始
- 2.3.我们有必要在应用内调用GC吗?
- 3.可以回收ImageView的bitmap吗
- 4.ImageView的加载方式
- 明明就一张本地图片,为什么会有这种区别呢?
- 总结
- 加载的时候,无论是否是本地图片,最好使用第三方库加载,节约内存
- 回收的时候,最好将ImageView置空,因为无引用,GC更容易回收
ImageView资源的利用和优化
1.利用Android Studio Profiler 工具解析应用的内存数据
因为想查看为ImageView设置图片时,内存的占用情况,故需要使用Profiler 工具。
当前项目,点击后运行即可。
内存类型包含:
- Java:
从Java或Kotlin代码中分配的对象的内存 - Native:
从C或c++代码中分配的对象的内存
即使你没有在app中使用c++,你可能会看到一些本地内存,因为Android框架使用Native内存来处理各种任务,比如处理图像资产和其他图形——即使你写的代码是Java或Kotlin - Graphics:
用于图形缓冲区队列的内存用于显示屏幕上的像素,包括GL表面、GL纹理等
(注意,这是与CPU共享的内存,而不是专用的GPU内存) - Stack:
在你的应用程序中,Native和Java栈使用的内存
这通常与你的应用程序运行的线程数有关 - Code:
您的应用程序用于代码和资源的内存,如dex字节码,优化或编译的dex代码 - Other:
应用程序使用的内存,系统不确定如何分类 - Allocated:
应用程序分配的Java/Kotlin对象的数量。这并不计算用C或c++分配的对象
2.分析ImageView操作以及内存的关系
2.1.前提条件
拥有APP主页面,MainActivity
拥有ImageView测试页面,ImgActivity;ImgActivity的布局文件中有个未设置图片的imageView。我们将通过按钮动态的设置图片,来看一下MEMORY的变化。
我们图片的大小:
vehicle_1:1065*1440 PNG (32-bit color)1.22MB
2.2.测试开始
- 启动我们测试页面,ImgActivity
- 设置图片
imageView.setImageResource(R.mipmap.vehicle_1);
- 点击按钮通过代码回收ImageView
方法1:
imageView.setImageDrawable(null);
imageView = null;
方法2:从父布局移除当前对象
if (ll != null && imageView != null) {
ll.removeView(imageView);
imageView = null;
}
- 强制垃圾回收
- 关闭当前页面
结论:
如上两种方法都可以达到回收ImageView内存占用的目的。
2.3.我们有必要在应用内调用GC吗?
因为JVM会控制GC哦,我们调用System.gc()
也可能仅仅是提醒JVM去回收,正常来讲是不需要我们调用的。
3.可以回收ImageView的bitmap吗
public void releaseImageViewResouce(ImageView imageView) {
if (imageView == null)
return;
Drawable drawable = imageView.getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}
在调用这个方法后,重新进入Activity再调用 imageView.setImageResource(R.mipmap.vehicle_1);
偶现异常:
Canvas: trying to use a recycled bitmap android.graphics.Bitmap@18236ae
原因是:Bitmap被recycle了,但是又被复用了。
这是因为虽然你已经将bitmap.recycle()
,但是进程并不知道,GC没有来得及回收Bitmap@18236ae
。
结论:bitmap.recycle()
是可以用于回收bitmap
的,但是请谨慎使用,bitmap对象
是否已经recycle()
需要在使用前判断。
4.ImageView的加载方式
imageView.setImageDrawable(getDrawable(R.mipmap.vehicle_1));
内存占用 ≈ 13.2MB
<ImageView
android:src="@mipmap/vehicle_1"
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="300dp" />
内存占用 ≈ 5.9MB
Glide.with(this)
.load(R.mipmap.vehicle_1)
.into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource,
@Nullable Transition<? super Drawable> transition) {
imageView.setImageDrawable(resource);
}
});
内存占用 ≈ 5.9MB
Glide.with(this).load(R.mipmap.vehicle_1).into(imageView);
内存占用 ≈ 2.3MB
…所以呢,最好使用第三方图片加载框架,因为他们已经做好了图片的优化和压缩。
明明就一张本地图片,为什么会有这种区别呢?
因为图片原本的大小和内存中的大小无关。
1.22MB 是本地图片的大小。但是内存中的大小实际上是:分辨率*像素点的大小。
完全跟图片大小无关(与图片格式png、jpg也无关)
Glide等框架都是通过,降低分辨率或者修改像素点格式,优化了内存中的占用。
注:实际上内存大小还受一些其他因素的影响,这里为了易懂,如此描述。
总结
加载的时候,无论是否是本地图片,最好使用第三方库加载,节约内存
Glide.with(this).load(R.mipmap.vehicle_1).into(imageView);
回收的时候,最好将ImageView置空,因为无引用,GC更容易回收
imageView.setImageDrawable(null);
imageView = null;