AssetManager是android的资源管理器,负责管理android系统所有的资源.资源可以分系统级别和应用级别.

系统级别主要是framework-res.apk,即编译framework/base/core/res目录下的,当然有时候定制系统会有定制的资源,一般放在~/vendor/overlay/...下面,可以在framework/base/core/res的android.mk中包含vendor/overlay/framework下面的文件资源,这样可以生成一个总的framework-res.apk出来,也可以分开生成两个,一个是系统原生的framework-res.apk,一个可以是定制的-res.apk,如果是分开的,那么就需要修改framework里面的程序,将定制的res添加到路径中,不然系统或者应用仍然无法查找.

另外就是framework/base/data/下面的资源,一般是字体,系统声音,以及自带媒体视频.定制系统经常会遇到调整这些什么系统字体,声音,或者自带媒体视频之类的,从Nokia时代就开始了.

应用级别主要是开发人员在app工程中res目录下的各种资源以及assets目录.

上面的资源共同的特点就是需要一个配置信息Configuration : 包括国家语言,时区,屏幕密度(以及屏幕大小)等等,这样的区分就可以在不同的地区使用不同的资源,不同设备使用不同资源---显而易见,屏幕适配.iphone app不需要做特别的适配!

资源的加载是从创建(或者说运行APP的第一个Activity开始).ActivityThread创建Activity对象,Activity的父类ContextImpl中的init开始初始化资源加载等操作.

下面从ContextImpl的init开始:

final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,Resources container, String basePackageName, UserHandle user) {mPackageInfo = packageInfo;mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;mResources = mPackageInfo.getResources(mainThread);if (mResources != null && container != null&& container.getCompatibilityInfo().applicationScale !=mResources.getCompatibilityInfo().applicationScale) {if (DEBUG) {Log.d(TAG, "loaded context has different scaling. Using container's" +" compatiblity info:" + container.getDisplayMetrics());}mResources = mainThread.getTopLevelResources(mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,null, container.getCompatibilityInfo());}mMainThread = mainThread;mActivityToken = activityToken;mContentResolver = new ApplicationContentResolver(this, mainThread, user);mUser = user;}

资源的对象Resource的获取:

mResources = mainThread.getTopLevelResources(mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,null, container.getCompatibilityInfo());

跳转到ActivityThread类中:

Resources getTopLevelResources(String resDir,int displayId, Configuration overrideConfiguration,CompatibilityInfo compInfo) {

在这个方法中:

WeakReference<Resources> wr = mActiveResources.get(key);r = wr != null ? wr.get() : null;//if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());if (r != null && r.getAssets().isUpToDate()) {if (false) {Slog.w(TAG, "Returning cached resources " + r + " " + resDir+ ": appScale=" + r.getCompatibilityInfo().applicationScale);}return r;}

ActivityThread通过一个Hashmap的结构体mActiveResources来维持APP和其对应的资源Resource.通过r.getAssets().isUpToDate来判断是否存在对应的资源并且资源没有outtime(不过这个outtime不知道为什么要检查,暂时不清楚),一般APP都会有对应的,返回true,那么就会返回获取到的r.

但是如果没有找到与上面resDir相符的,那么程序就会创建AssetManager并且加载资源:

AssetManager assets = new AssetManager();if (assets.addAssetPath(resDir) == 0) {return null;}

方法:

addAssetPath

非常重要,在很多场所都用到,但是这个方法却是标记@hide的,所以一般如果要使用都是通过反射的方式,比如:如果放一张图片到assets文件夹中,那么不会产生R.id,那么就需要开发人员通过上面的方法加载到资源路径下去,才能够提供给其他的使用,常见的比如资源插件,资源在另外一个plugin中,那么这个plugin中的所有资源就需要先添加到资源路径下,才能够被宿主机使用.这个就废话不多说了,记住就好了.

public native final int addAssetPath(String path);/*** Add multiple sets of assets to the asset manager at once.  See* {@link #addAssetPath(String)} for more information.  Returns array of* cookies for each added asset with 0 indicating failure, or null if* the input array of paths is null.* {@hide}*/public final int[] addAssetPaths(String[] paths) {if (paths == null) {return null;}int[] cookies = new int[paths.length];for (int i = 0; i < paths.length; i++) {cookies[i] = addAssetPath(paths[i]);}return cookies;}

很显然这个方法是一个native方法:最终是在AssetManager.cpp

bool AssetManager::addAssetPath(const String8& path, void** cookie)
{AutoMutex _l(mLock);asset_path ap;

在这个函数中,路径被添加到一个Vector的向量表中:

// Skip if we have it already.for (size_t i=0; i<mAssetPaths.size(); i++) {if (mAssetPaths[i].path == ap.path) {if (cookie) {*cookie = (void*)(i+1);}return true;}}ALOGV("In %p Asset %s path: %s", this,ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());mAssetPaths.add(ap);

下面还有一段程序,当加载的是framework-res.apk的包,系统还会继续检查是否有替换资源,即覆盖资源---覆盖机制:

// add overlay packages for /system/framework; apps are handled by the// (Java) package managerif (strncmp(path.string(), "/system/framework/", 18) == 0) {// When there is an environment variable for /vendor, this// should be changed to something similar to how ANDROID_ROOT// and ANDROID_DATA are used in this file.String8 overlayPath("/vendor/overlay/framework/");overlayPath.append(path.getPathLeaf());if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {asset_path oap;oap.path = overlayPath;oap.type = ::getFileType(overlayPath.string());bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlayif (addOverlay) {oap.idmap = idmapPathForPackagePath(overlayPath);if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);}}if (addOverlay) {mAssetPaths.add(oap);} else {ALOGW("failed to add overlay package %s\n", overlayPath.string());}}}

这个在前言说了,会去看看vendor/overlay/framework下是否有替换的资源.这里是系统的资源覆盖机制.不过我搜索了一下,网上还有如下:

如果手机厂商要利用上述的资源覆盖机制来自定义自己的系统资源,那么还需要提供一个idmap文件,用来说明它在/vendor/overlay/framework/目录提供的Apk文件要覆盖系统的哪些默认资源,使用资源ID来描述,因此,这个idmap文件实际上就是一个资源ID映射文件。这个idmap文件最终保存在/data/resource-cache/目录下,并且按照一定的格式来命令,例如,假设手机厂商提供的覆盖资源文件为/vendor/overlay/framework/framework-res.apk,那么对应的idmap文件就会以名称为@vendor@overlay@framework@framework-res.apk@idmap的形式保存在目录/data/resource-cache/下。关于Android系统的资源覆盖(Overlay)机制,可以参考frameworks/base/libs/utils目录下的READ文件

从这段话看出,网上面替换是将vendor/overlay/framework/的资源生成一个新的res.apk来提供替换方案.其实没看太明白,而且READ文件也没有找到(暂时参照网上:http://blog.csdn.net/tuhuolong/article/details/45939879).而且所谓的提供idmap文件看起来很高大上,刚开始我还以为需要人工去生成,有一点的茫然.我后来这个地方打印log出来:

然后将机器重启:

加载framework-res.apk的时候,上面的判断成立.再进一步打出overlaypath:

如果可以改程序,那么其实vendor.../可以改为任意目录,让系统根据任意的目录查找就好了.

至于后面要提供什么idmap,我是迷糊了,如果overlayPath有效,执行下面的就有idmap了,至于什么需要提供什么,以及什么格式之类的,有点吓人,这些都是下面的函数搞定并返回idmap

oap.idmap = idmapPathForPackagePath(overlayPath);
if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);}

然后通过下面将overlay资源添加到资源路径下去.

if (addOverlay) {mAssetPaths.add(oap);} 

如果是APP呢,那么上面的程序函数前面那个注释就给了提示,如果是app的就可以到PackageManager中的getResourcesForApplication方法,但是这个方式是个抽象方法,实际要看ApplicationPackageManager中的:

@Override public Resources getResourcesForApplication(ApplicationInfo app) throws NameNotFoundException {if (app.packageName.equals("system")) {return mContext.mMainThread.getSystemContext().getResources();}Resources r = mContext.mMainThread.getTopLevelResources(app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);if (r != null) {return r;}throw new NameNotFoundException("Unable to open " + app.publicSourceDir);}

我在这个地方打印log出来:

这个一看就显而易见了,如果是定制系统就可以做一个隐射表,比如上面是桌面Launcher的APK,如果要整体更换桌面的主题,那么就可以再做一个launcher_plugin.apk放到系统里面,但是这两个工程的资源要一一对应,比如launcher有一个R.id.pumpkin的资源,那么launcher_plugin.apk必须要也有,因为这是整体替换.这样如果上面的app.sourceDir等于桌面的时候,就将这个app.sourceDir的值替换为launcher_plugin.apk中,那么launcher就会使用这个插件资源了.这个是整体工程替换.

题外话:我记得我以前公司如果定制系统资源都是在vendor/overlay/framework下去增加新的资源,而不是替换的方式,相当于做了一套资源(主题资源等),然后让团队所有的APP使用公司定制的资源,而不是替换的方式,这样就有一个问题,定制资源改了,上面所有的APP也要对应着改;但是有一个好处就是保留了原生资源,这样其他的APP的主题就不会影响,但是如果是替换的方式,那么就会影响第三方的APP,因为那个时候第三方的使用系统资源部分的都将被OEM定制了,也就是说运行在不同手机上APP视觉效果不一样了.

下篇在看看如何获取单个资源ID信息.



Android AssetManager 1相关推荐

  1. android 创建assetmanager文件,Android AssetManager

    Android AssetManager的创建 本文基于Android 6.0源码分析 AssetManager的类图 我们以一个"Hello World" APK(包名:com. ...

  2. Android AssetManager初探

    Android开发中,获取Assets资源要从AssetManager中获取,而AssetManager可以从Resource中进行获取.而Resource可以获取Id.drawablw.string ...

  3. 在ubuntu 14.04 编译android 2.3.1 错误解决办法

    首先必须降低gcc版本: sudo apt-get install gcc-4.4 sudo apt-get install g++-4.4 sudo rm -rf /usr/bin/gcc /usr ...

  4. 在英特尔® 凌动™ 处理器上将 OpenGL* 游戏移植到 Android* (第一部分)

    将游戏和其他使用大量 3D 图形的应用从 OpenGL 标准移植到 Google Android 设备(包括构建在英特尔® 凌动™ 微架构上的设备)存在巨大的机遇,因为基于 OpenGL 的游戏.游戏 ...

  5. 由Asset中的double free引发的Android系统及APP崩溃问题分析

    前言 这个问题在来小米之前就遇到并解决过,当时的解决方案与朴老师的初步解决方案一样,本文在之前的初步分析结果之上进一步进行了深入分析,最终得出了当前看起来相对合理并符合原来架构设计的最终方案. 文中引 ...

  6. ubuntu13.10编译android文件系统4.0.4错误全部解析

    以下是编译Android 4.0.4的常见错误,这些错误的解决办法收录自互联网. Google group有个专门的Android Building组,有兴趣的可以加入. 除了android组解决的错 ...

  7. Android冻结程序,Android Studio 3.0和应用程序冻结

    我已经将我的 android工作室从2.x更新到android 3.0 stable. 从那时起,当我尝试使用cyanogenmod 12.1(android 5.1.1)将应用程序推送到我的Nexu ...

  8. Android源码编译make的各类错误解决方案汇总

    问题: You are attempting to build with the incorrect version of java.  Your version is: java version & ...

  9. android资源的热更新(替换 AssetManager+LoadedApk中的资源路径)

    设置LoadedApk中的mResDir 创建AssetManager, 设置资源包路径 替换AssetManager AssetManager newAssetManager = (AssetMan ...

最新文章

  1. 无约束最优化方法-牛顿法
  2. 如果使用StateServer或SQLServer,会遇到的问题。。。。
  3. Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解
  4. Linux Shell 脚本基础
  5. 2021款iPad Pro渲染图曝光:依旧采用双摄+激光雷达扫描仪
  6. 消息中间件学习总结(10)——Kafka、RabbitMQ、RocketMQ消息中间件的消息发送性能对比
  7. SAP License:糟糕的用户比任何系统问题都要危险
  8. linux下插入的mysql数据乱码问题及第三方工具显示乱码问题
  9. 通过DBLINK跨数据库查询,同步创建表结构,插入表数据
  10. 【金九银十】java数据结构和算法第二版
  11. Command Line Tools下载不下来,则可以去官网下载
  12. “以图搜图”引擎及网站合集
  13. R安装并行计算工具包snowfall实现并行运算资源
  14. 【MySQL】在MySQL中如何给表起别名
  15. Maya粒子特效制作(二)
  16. Mysql设置初始化密码和修改密码
  17. NIT 股市风云 按位与运算 F. 休赛季的引援#2
  18. 一个完整的LED灯具散热优化分析计算方案
  19. 蚂蚁集团开源大规模视频侵权定位数据集
  20. @[TOC](CDN防御与高防服务器防御的区别

热门文章

  1. 微信群机器人微云助手微小云微信群淘客助手如何设置好券直播淘宝联盟优惠券自动群发
  2. Android自定义睡眠下表统计图,Android 进阶自定义View(5)图表统计PieChartView圆饼图的实现...
  3. WindML、FreeType和TrueType三者相结合实现矢量字体的显示
  4. Win10下通过源码编译安装QGIS
  5. Games101计算机图形学学习笔记:线性代数-向量
  6. 《你的善良必须有点锋芒》-慕言歌
  7. 澳洲网:超8成雇主对澳大利亚高校毕业生表示满意
  8. CFileDialog的使用(MFC-C++)
  9. 互联网公司最常见的面试算法题大集合!
  10. Sailfish 浏览器是基于 Gecko 引擎开发