Android开发:如何避免动态调用getIdentifier()获取R.string资源?
我的应用采用单Activity架构,用户在主Activity的RecyclerView中点击recipe1类按钮时,需要跳转到菜谱详情页并加载对应内容。因为不想为每个菜谱单独创建Activity,所以打算通过Intent传递菜谱名称(如recipe1),再从strings.xml中匹配对应的资源(如cooking_time_recipe1、ingredients_recipe1等)来动态加载详情。
目前我用getIdentifier()实现了功能,但Android Studio提示该方法不推荐使用——反射会影响构建优化与编译校验,直接通过资源ID获取效率更高。想请教有没有替代方案:我可以写函数根据菜谱名称生成对应资源名,但还是需要获取资源ID;手动维护HashMap映射字符串和资源ID又感觉和getIdentifier()原理重复,不确定是否有必要。
附相关代码
适配器代码(不含ViewHolder子类)
public class RecipesViewPagerAdapter extends RecyclerView.Adapter<RecipesViewPagerAdapter.ViewHolder> { Context context; ArrayList<String> recipes; public RecipesViewPagerAdapter(Context context, ArrayList<String> recipes) { this.context = context; this.recipes = recipes; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_recipes_pager, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { CardTriplet currentRecipes = recipes.get(position); holder.cardRecipe1.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 0)); if (currentRecipes.get(1) != null) { holder.textRecipe2.setText(currentRecipes.get(1).getName()); holder.cardRecipe2.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 1)); } else { holder.cardRecipe2.setVisibility(View.INVISIBLE); } if (currentRecipes.get(2) != null) { holder.textRecipe3.setText(currentRecipes.get(2).getName()); holder.cardRecipe3.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 2)); } else { holder.cardRecipe3.setVisibility(View.INVISIBLE); } } @Override public int getItemCount() { return Recipes.size(); } private void onCardClick(int tripletNumber, int cardNumber) { Card card = Recipes.get(tripletNumber).get(cardNumber); Intent intent = new Intent(context, RecipeActivity.class); intent.putExtra("name", card.getName()); context.startActivity(intent); } }
主Activity获取菜谱列表代码
String[] recipeNames = getResources().getStringArray(R.string.recipes);
(通过遍历该数组初始化ArrayList<CardTriplet> recipes内容)
方案1:使用枚举类绑定资源ID
创建枚举类统一管理每个菜谱的关联资源ID,完全避免字符串匹配,编译期就能校验资源存在性,安全且高效。
示例代码:
public enum Recipe { RECIPE1( R.string.recipe1_name, R.string.cooking_time_recipe1, R.string.ingredients_recipe1 ), RECIPE2( R.string.recipe2_name, R.string.cooking_time_recipe2, R.string.ingredients_recipe2 ); public final int nameResId; public final int cookingTimeResId; public final int ingredientsResId; Recipe(int nameResId, int cookingTimeResId, int ingredientsResId) { this.nameResId = nameResId; this.cookingTimeResId = cookingTimeResId; this.ingredientsResId = ingredientsResId; } // 根据菜谱名称字符串获取枚举实例 public static Recipe fromName(String name) { for (Recipe recipe : values()) { if (name.equals(recipe.name().toLowerCase())) { return recipe; } } throw new IllegalArgumentException("Unknown recipe: " + name); } }
使用方式:
- 主Activity初始化时,用枚举的
nameResId填充菜谱列表;传递Intent时直接传枚举名。 - RecipeActivity中接收后直接调用资源:
String recipeName = getIntent().getStringExtra("name"); Recipe recipe = Recipe.fromName(recipeName); tvCookingTime.setText(getString(recipe.cookingTimeResId)); tvIngredients.setText(getString(recipe.ingredientsResId));
方案2:预构建静态资源映射类
如果菜谱数量较多,可创建静态类在App启动时一次性初始化资源映射,仅调用一次getIdentifier(),避免重复反射消耗。
示例代码:
public class RecipeResources { private static final Map<String, RecipeRes> RES_MAP = new HashMap<>(); public static void init(Context context) { Resources res = context.getResources(); String[] recipeNames = res.getStringArray(R.array.recipes); // 建议改用array资源存储菜谱名称列表 for (String name : recipeNames) { int cookingTimeId = res.getIdentifier("cooking_time_" + name, "string", context.getPackageName()); int ingredientsId = res.getIdentifier("ingredients_" + name, "string", context.getPackageName()); RES_MAP.put(name, new RecipeRes(cookingTimeId, ingredientsId)); } } public static RecipeRes getRecipeRes(String name) { return RES_MAP.get(name); } public static class RecipeRes { public final int cookingTimeId; public final int ingredientsId; public RecipeRes(int cookingTimeId, int ingredientsId) { this.cookingTimeId = cookingTimeId; this.ingredientsId = ingredientsId; } } }
使用方式:
- 在Application的
onCreate()中调用RecipeResources.init(this);完成初始化。 - RecipeActivity中直接获取资源:
String recipeName = getIntent().getStringExtra("name"); RecipeResources.RecipeRes res = RecipeResources.getRecipeRes(recipeName); tvCookingTime.setText(getString(res.cookingTimeId));
方案3:使用资源数组关联
将每个菜谱的资源ID按顺序存入数组,与菜谱名称数组一一对应,完全无反射,实现简单。
示例(在arrays.xml中定义):
<string-array name="recipe_names"> <item>recipe1</item> <item>recipe2</item> </string-array> <integer-array name="recipe_cooking_time_ids"> <item>@string/cooking_time_recipe1</item> <item>@string/cooking_time_recipe2</item> </integer-array> <integer-array name="recipe_ingredients_ids"> <item>@string/ingredients_recipe1</item> <item>@string/ingredients_recipe2</item> </integer-array>
主Activity初始化时关联资源:
String[] names = getResources().getStringArray(R.array.recipe_names); int[] cookingTimeIds = getResources().getIntArray(R.array.recipe_cooking_time_ids); int[] ingredientsIds = getResources().getIntArray(R.array.recipe_ingredients_ids); ArrayList<CardTriplet> recipes = new ArrayList<>(); for (int i = 0; i < names.length; i++) { Card card = new Card(names[i], cookingTimeIds[i], ingredientsIds[i]); // 将card加入CardTriplet并填充到recipes列表 }
传递Intent时直接传递资源ID或序列化的Card对象,RecipeActivity可直接使用ID获取资源。
方案对比
- 枚举类:最安全,编译期校验资源,适合菜谱数量固定的场景。
- 静态映射类:兼顾灵活性与性能,适合菜谱可能动态增加的场景。
- 资源数组关联:完全无反射,实现简单,适合资源结构统一的场景。
以上方案均比跳转时直接调用getIdentifier()更高效,且避免了编译期无法校验资源的问题。
内容的提问来源于stack exchange,提问作者user20958695

