Navigation Drawer Header 背景图片内存占用过高问题如何解决?
内存异常上涨的根本原因
你看到的32KB是JPEG的压缩存储体积,和加载到内存后的实际占用是两个完全不同的概念:
- Android加载图片资源时会先把压缩格式的JPEG解码为原始位图,默认使用ARGB_8888配置,每个像素占用4字节。如果不对图片做任何压缩处理,1600*900分辨率的位图本身就要占用
1600 * 900 * 4 = 5.76MB内存 - 更核心的原因是资源目录适配问题:如果你的图片放在了低DPI的资源文件夹(比如默认的drawable文件夹等价于drawable-mdpi),在高DPI设备上系统会自动按DPI比例缩放位图来适配显示,比如在xxhdpi设备上会放大3倍,缩放后的分辨率变成4800*2700,仅这一张图就要占用
4800 * 2700 * 4 = 51.84MB内存,这就是你启动后内存直接飙升的核心原因。
优化方案
- 正确放置图片资源
不要把图片直接放在根drawable目录下,根据你图片的目标DPI放到对应文件夹,比如针对主流xxhdpi设备就放到drawable-xxhdpi文件夹;如果希望图片不被系统自动缩放,直接放到drawable-nodpi目录即可。
- 正确放置图片资源
- 调整解码配置降低内存占用
这张背景图不需要透明通道,可以改用RGB_565解码配置,每个像素仅占用2字节,内存占用直接减少一半。如果是xml设置背景,可以在res/drawable下新建一个xml drawable指定配置:
然后把布局的background改成指向这个xml drawable即可。<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/navigation_header_bg" android:config="RGB_565" />- 调整解码配置降低内存占用
- 按实际显示尺寸采样加载
你的导航头高度只有200dp,实际显示高度远低于原图900px的高度,可以通过采样压缩后再加载。更推荐直接用Glide来加载背景,自动完成采样适配:
// Kotlin代码示例,在NavigationView初始化后调用 val headerView = navigationView.getHeaderView(0) // 先给你的根布局加id为header_root val headerLayout = headerView.findViewById<ConstraintLayout>(R.id.header_root) Glide.with(this) .asBitmap() .load(R.drawable.navigation_header_bg) .into(object : CustomTarget<Bitmap>() { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { headerLayout.background = BitmapDrawable(resources, resource) } override fun onLoadCleared(placeholder: Drawable?) {} })- 按实际显示尺寸采样加载
- 配套优化RecyclerView图片加载
针对列表滑动内存上涨的问题,给Glide加载加上固定尺寸限制,避免加载过大的图片到内存:
同时可以给RecyclerView添加滑动监听,滑动时暂停Glide加载,减少卡顿和内存波动:// Adapter里加载图片的代码示例 Glide.with(context) .load(图片路径) .override(目标ImageView的宽高,比如400.dpToPx()) .centerCrop() .into(imageView)recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) if (Math.abs(dy) > 10) { Glide.with(context).pauseRequests() } else { Glide.with(context).resumeRequests() } } override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState == RecyclerView.SCROLL_STATE_IDLE) { Glide.with(context).resumeRequests() } } })- 配套优化RecyclerView图片加载
内容的提问来源于stack exchange,提问作者Omar Shawky
相关产品推荐
相关产品推荐

