Android 12宿主应用加载小组件预览布局遇ResourcesNotFoundException
问题原因
previewLayout返回的是小组件提供应用内部的资源ID,每个Android应用的资源ID都是独立的,宿主应用的Context只能访问自身包内的资源,直接用宿主的LayoutInflater加载其他应用的资源ID,必然触发ResourcesNotFoundException。而loadPreviewImage是系统封装的跨应用资源加载方法,内部已处理跨包访问逻辑,所以能正常加载预览图。
解决方案
要加载其他应用的布局资源,必须先获取该应用的Context,再用这个跨应用的Context加载布局。具体步骤如下:
获取小组件应用的包名
从AppWidgetProviderInfo的组件信息中提取包名:String widgetPackageName = widgetInfo.getAppWidgetInfo().provider.getPackageName();创建小组件应用的Context
使用createPackageContext方法创建对应包的Context,同时捕获包不存在的异常:Context widgetContext = null; try { // CONTEXT_RESTRICTED 创建受限Context,仅用于访问资源,避免权限风险 widgetContext = binding.getRoot().getContext().createPackageContext(widgetPackageName, Context.CONTEXT_RESTRICTED); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); // 小组件应用已卸载,降级到预览图逻辑 if (previewDrawable != null) { Glide.with(binding.getRoot().getContext()) .load(previewDrawable) .into(binding.previewImage); } return; }用跨应用Context加载布局
使用小组件应用的Context初始化LayoutInflater,再inflate目标布局:View widgetPreview = LayoutInflater.from(widgetContext).inflate(previewLayout, binding.getRoot(), false); binding.widgetContainer.addView(widgetPreview);
修改后的完整代码
Drawable previewDrawable = widgetInfo.getAppWidgetInfo().loadPreviewImage(binding.getRoot().getContext(), ICON_DENSITY); int previewLayout = widgetInfo.getAppWidgetInfo().previewLayout; try { if (previewLayout != 0) { String widgetPackageName = widgetInfo.getAppWidgetInfo().provider.getPackageName(); Context widgetContext = binding.getRoot().getContext().createPackageContext(widgetPackageName, Context.CONTEXT_RESTRICTED); View widgetPreview = LayoutInflater.from(widgetContext).inflate(previewLayout, binding.getRoot(), false); binding.widgetContainer.addView(widgetPreview); } else if (previewDrawable != null) { Glide.with(binding.getRoot().getContext()) .load(previewDrawable) .into(binding.previewImage); } } catch (PackageManager.NameNotFoundException e) { // 小组件应用不存在,显示预览图或占位 if (previewDrawable != null) { Glide.with(binding.getRoot().getContext()) .load(previewDrawable) .into(binding.previewImage); } e.printStackTrace(); } catch (Exception e) { // 其他异常降级到预览图 if (previewDrawable != null) { Glide.with(binding.getRoot().getContext()) .load(previewDrawable) .into(binding.previewImage); } e.printStackTrace(); }
注意事项
- 如果小组件应用的资源设置了
android:private="true",宿主应用将无法访问这些资源,此时只能降级使用previewImage。 - Android 12及以上版本中,
CONTEXT_RESTRICTED是更安全的选项,它限制了Context的权限,仅允许访问资源,避免潜在的安全风险。
内容的提问来源于stack exchange,提问作者Deepika
相关产品推荐
相关产品推荐

