如何为可自由缩放的Widget计算Glide加载RemoteViews的Bitmap最大适配尺寸
解决RemoteViews Widget Bitmap内存超限问题
问题分析
你遇到的错误是因为RemoteViews加载的Bitmap总内存超过了系统限制——根据官方规则,RemoteViews内所有Bitmap的内存总和不能超过屏幕宽 × 屏幕高 × 4 × 1.5字节(4是ARGB_8888格式每个像素的字节数,1.5倍屏幕填充内存是系统上限)。低内存设备本身内存余量小,加上Widget支持自由调整尺寸(可拉至很大),直接加载原图会触发内存超限。
核心思路
要解决这个问题,需要:
- 计算设备允许的Bitmap最大内存上限
- 获取Widget当前的实际显示尺寸(支持自由调整的情况下,取其最大可调整尺寸)
- 根据内存上限和Widget尺寸,计算出Bitmap的最大允许尺寸,让Glide按这个尺寸加载图片,同时优化Bitmap格式减少内存占用
具体实现步骤与代码
1. 计算设备允许的最大Bitmap内存
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; // 计算RemoteViews允许的Bitmap总内存上限(单位:字节) long maxAllowedMemory = (long) screenWidth * screenHeight * 4 * 1.5;
2. 获取Widget的最大可调整尺寸
针对支持自由调整大小的Widget,用AppWidgetManager获取其当前的最大可调整尺寸:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); Bundle widgetOptions = appWidgetManager.getAppWidgetOptions(appWidgetId); // 获取Widget的最大宽高(用户可调整到的最大值) int widgetMaxWidth = widgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH); int widgetMaxHeight = widgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
注:API 16及以上支持该方法,低于16的设备可使用屏幕尺寸的80%作为默认Widget尺寸
3. 计算Bitmap的最大允许尺寸
根据内存上限和Widget的宽高比例,算出不会超限的Bitmap尺寸,同时保证不超过Widget实际尺寸:
// 单个ARGB_8888格式Bitmap的内存 = 宽 × 高 × 4,所以最大像素总数 = 内存上限 /4 long maxPixelCount = maxAllowedMemory / 4; // 按Widget的宽高比例计算最大尺寸,避免图片变形 float widgetRatio = (float) widgetMaxWidth / widgetMaxHeight; int maxBitmapWidth = (int) Math.sqrt(maxPixelCount * widgetRatio); int maxBitmapHeight = (int) (maxBitmapWidth / widgetRatio); // 确保Bitmap尺寸不超过Widget的实际最大尺寸 maxBitmapWidth = Math.min(maxBitmapWidth, widgetMaxWidth); maxBitmapHeight = Math.min(maxBitmapHeight, widgetMaxHeight);
4. 优化Glide加载配置
修改Glide的加载代码,按计算出的尺寸加载,并使用更节省内存的图片格式:
RequestOptions options = new RequestOptions() .placeholder(R.drawable.appwidget_bg) .error(R.drawable.appwidget_bg) .override(maxBitmapWidth, maxBitmapHeight) // 限制加载的Bitmap最大尺寸 .format(DecodeFormat.PREFER_RGB_565) // 使用RGB_565格式,比ARGB_8888节省一半内存 .fitCenter(); // 适配ImageView尺寸,避免拉伸变形 AppWidgetTarget awt = new AppWidgetTarget(context, R.id.imageView, views, appWidgetId); Glide.with(context.getApplicationContext()) .asBitmap() .load("https://test.com/big_image.png") .apply(options) .into(awt);
关键注意事项
- 若RemoteViews内有多个Bitmap,需将所有Bitmap的内存总和控制在
maxAllowedMemory以内,可按比例分配单个Bitmap的内存上限 - RGB_565格式会牺牲部分色彩精度,但对于Widget图片来说,视觉差异基本可忽略,却能直接减少50%的内存占用
- Glide的
override()方法会让Glide直接加载缩放后的Bitmap,避免加载原图占用过多内存,同时Glide会自动缓存缩放后的图片,提升后续加载效率
内容的提问来源于stack exchange,提问作者DAVID _
相关产品推荐
相关产品推荐

