AI智能
改变未来

Android动态获取资源ID之getIdentifier()


前言

  再说getIdentifier()前,科普下什么是SDK:SDK(Software Development Kit)是软件开发工具包的意思,一般我们将一部分功能单独封装成一个库文件进行开发和维护,然后将库文件提供给第三方使用。

1.应用场景描述

  SDK都是基于AS进行功能开发的 ,因为要提供给别人使用,而AS作为库文件的最终产物就是.aar文件。SDK开发精髓就是麻烦自己方便别人。既然提供了AS库支持自然也要提供EC库的支持 ,但是EC它不支持aar文件,怎么办嘞,aar文件也是压缩包,将aar解压出来,把里面的classes.jar拷贝出来重命名,然后在Eclipse中依赖这个jar包,同时,SDK的资源文件、Assets相关也需要拷贝到Eclipse项目中。

1.1异常原因分析

将AS库放到EC工程里发生崩溃Caused by: android.content.res.Resources$NotFoundException:

经过分析找到了问题原因所在:举个例子,sdk用到 setContentView(R.layout.activity_main)这种方式展示布局, 当我们把资源文件拷贝到Eclipse,再编译apk的时候,资源文件会对应一个新的资源id,而aar中classes.jar里引用R中id的是不变的 也就是说在.aar文件中setContentView(2130903040),

到了EC中重新编译后aapt会重新赋予activity_main一个新的值,自然报找不到资源ID 的异常了 。要解决这个问题需要SDK动态的获取资源ID ,不能直接写死,查找过一些资料发现,谷歌提供了相关的API,可以通过资源名称获取资源id

/*** Return a resource identifier for the given resource name.  A fully* qualified resource name is of the form \"package:type/entry\".  The first* two components (package and type) are optional if defType and* defPackage, respectively, are specified here.** <p>Note: use of this function is discouraged.  It is much more* efficient to retrieve resources by identifier than by name.** @param name The name of the desired resource.* @param defType Optional default resource type to find, if \"type/\" is*                not included in the name.  Can be null to require an*                explicit type.* @param defPackage Optional default package to find, if \"package:\" is*                   not included in the name.  Can be null to require an*                   explicit package.** @return int The associated resource identifier.  Returns 0 if no such*         resource was found.  (0 is not a valid resource ID.)*/public int getIdentifier(String name, String defType, String defPackage) {return mResourcesImpl.getIdentifier(name, defType, defPackage);}

   API格式getIdentifier(“资源ID名称”,“资源类型”,“应用包名”)
   getIdentifier(“activity_main”,“layout”,“com.demo.xxxx”)

2.Demo示例

通过上面提供的官方API ,尝试写一个DEMO 验证下

在Activity中 调用封装好的getIdentifier(),看下动态获取的ID是否和R.layout.activity_main值一样

布局文件R.layout.activity_main ,从AS工程R.class可以看到int类型的索引值2130903040


动态获取的R.layout.activity_main 同样为2130903040

2.1结论

ResourceUtils.getLayoutId(context,“activity_main”))等效于R.layout.activity_main

SDK是给第三方使用的,避免使用R.layout.main方式引用资源。而且在游戏SDK圈中,涉及到编译和反编译等技术多资源合并去重。

在三方使用的时候资源id经常会发生改变,如果还用R.layout.main方式会造成第三方接入难度 等很多不确定因素。

3. 工具类封装

import android.content.Context;import java.lang.reflect.Field;/*** @author sjr* Date 2020/7/16*/public class ResourceUtils {public static int getAnimId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"anim\", context.getPackageName());}public static int getAnimatorId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"animator\", context.getPackageName());}public static int getAttrId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"attr\", context.getPackageName());}public static int getBoolId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"bool\", context.getPackageName());}public static int getColorId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"color\", context.getPackageName());}public static int getDimenId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"dimen\", context.getPackageName());}public static int getDrawableId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"drawable\", context.getPackageName());}public static int getId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"id\", context.getPackageName());}public static int getIntegerId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"integer\", context.getPackageName());}public static int getInterpolatorId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"interpolator\", context.getPackageName());}public static int getLayoutId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"layout\", context.getPackageName());}public static int getPluralsId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"plurals\", context.getPackageName());}public static int getStringId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"string\", context.getPackageName());}public static int getStyleId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"style\", context.getPackageName());}public static int getStyleableId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"styleable\", context.getPackageName());}public static int getXmlId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"xml\", context.getPackageName());}public static int getMipmapId(Context context, String defType) {return context.getResources().getIdentifier(defType, \"mipmap\", context.getPackageName());}/*** 通过反射来读取int[]类型资源Id* @param context* @param name* @return*/public static final int[] getResourceDeclareStyleableIntArray(Context context, String name) {try {Field[] fields2 = Class.forName(context.getPackageName() + \".R$styleable\" ).getFields();for (Field f : fields2 ){if (f.getName().equals(name)){int[] ret = (int[])f.get(null);return ret;}}}catch (Throwable t){}return null;}}

结语

 记录下自己的学习和工作经验,分享给有需要的人。如果有那里写的不对或者不理解,欢迎大家的指正。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android动态获取资源ID之getIdentifier()