1.应用程序资源管理器assets

assets就是apk工程中的一个普通目录,在每个工程的根目录下都可以发现(或者可以自己创建)一个assets目录。

assets目录用于专门保存各种外部文件,比如图像、音视频、配置文件、字体、自带数据库等。它之所以适合用来管理这些文件,是因为应用程序在编译时不会去处理这个目录下的文件,但是却会将它们打包进APK中。而其它你随便创建的目录在编译时就会被直接忽略掉。同时,可以在assets目录内任意创建目录层级关系,这对于有大量外部文件需要集成的应用来说,就能很方便地分类管理了。

在Android应用开发中,比较常见的可以保存任意类型文件的地方主要有两个:res/raw目录、assets目录。所以,遇到外部文件的预置需求,如音视频、配置文件、字体等时,通常会考虑这两个目录。它们各自的特点是:

①res/raw是一个比较特殊的资源文件目录。用于保存一些二进制文件,这个目录下的所有文件都会被记录到“索引表”中,但是编译系统不会去动里面的文件。raw目录下的文件放进去时是什么样的,编译成APK以后还是什么样。这个目录比较适合保存一些音视频等二进制文件。

注:res目录下的资源文件夹,不管是raw还是drawable或mipmap,都不能自由地设计子目录层级关系。不管有多少文件,都只能放在同一级目录中。

②assets目录是一个非常自由的目录。它就像是Android应用中的“三不管”地带,不会为里面的文件建立索引、不会限制目录层级关系、不会处理里面的文件。如果想更好地管理自己的外部资源文件,建议使用assets目录。

注:asset的特殊应用:在APK开发中,有一种管理配置信息的做法比较常见:直接将配置信息文件放入assets目录中管理,程序首次运行时将这里面的配置信息拷贝到外部的可操作的目录下,后续程序的运行均靠这份保存在外部的配置信息为准,assets中的信息仅作为原始配置信息的备份。

但是,assets目录在使用上也有一点小缺憾。即assets目录内的文件在程序打包发布以后就是只读的,也就是只能读取里面的文件,而无法修改或增加文件。如果要想增加内容,可以通过Database或者SharedPreferences往/data/data目录下保存就好了。

注:由于应用程序在编译时不会去处理assets目录下的文件,而是直接将它们打包进APK中,这样会造成assets里的文件过大时,编译apk会特别特别慢,因此有时候为了不把assets里的某些文件打包进apk,可以在build.gradle里添加一个设置:

aaptOptions{

……

ignoreAssetsPattern 'file1:file2:file3'

}

2.assets 开发

Android使用android.content.res.AssetManager类读取assets目录下的文件。

关于AssetManager有几个方法:

①构造方法

通常可以通过两种方式来得到AssetManager的实例:通过Context实例的getAssets()方法、通过Resources实例的getAssets()方法。

②open()方法:open(string) 、open(string,int)

这两个方法都是将assets目录下的某个文件封装成InputStream的形式供开发者使用,也就是用来读文件的。

其中,参数string指“文件名”,更准确的说是文件的相对路径,即某个文件在assets目录下的相对路径。例如:dir1/file1.png、dir2/dir3/dir4/file2.avi。

第二个方法中int型参数指“访问模式”,就是将assets目录下的文件以什么模式来打开。一共有4种模式可供选择:

ACCESS_UNKNOW:无模式。其代表的值是 0。

ACCESS_RANDOM:无序访问模式。这种模式下文件的访问只会打开其中一段内容,然后再根据需要向流的前方或后方移动读取指针。其代表的值是1。

ACCESS_STREAMING:顺序读取模式。文件将会被从头部打开,然后按顺序向后面移动读取数据。其代表的值是2。

ACCESS_BUFFER:缓存读取模式。读取时会将整个文件直接读取到内存中,这种模式适合小文件的读取。其代表的值是3。

注:在open(string)中,它使用的文件读取模式是 ACCESS_STREAMING 模式。

③openFd()方法

将assets目录中的文件以FileDescriptor的形式打开,返回一个AssetFileDescriptor实例。

④openNonAssetFd()方法

openNonAssetFd(string)和openNonAssetFd(int,string)

和openFd()是一样的,只不过它跳出了assets目录的范围限定,而是站在工程根目录的视角来打开文件的FileDescriptor的。也就是说它允许打开 APK中任意位置的文件的AssetFileDescriptor实例。

⑤openXmlResourceParser()方法

打开assets目录下的xml形式的文件,直接返回 XmlResourceParser实例。其实就是官方做了从InputStream到XML解析器之间的转换,有助于增加一些开发效率。

接下来,通过实例演示一下assets目录下文件的读取方法。

①直接读取最普通的文件的方法

try{

InputStream is= this.getAssets().open( "image.png");

Log.d(TAG, "File available:" + is.available());

InputStream is2= this.getResources( ).getAssets().open("image2.png");

Log.d(TAG, "File available2:" + is2.available());

}catch(IOException e) {

e.printStackTrace();

}

执行的结果如下:

File available:3

File available2:11416

注意,最后一定不要忘记将用完的InputStream资源关掉!

②读取自定义目录层级的文件的方法

try{

InputStream is= this.getAssets().open( "lottie/one/forest.xml");

Log.d(TAG, "File available:" + is.available());

}catch(IOException e) {

e.printStackTrace();

}

执行结果如下:

File available:32114

同样,不要忘记调用InputStream的close()方法。

③以文件描述符形式读取的方法

try{

AssetFileDescriptor afd= this.getAssets( ).openFd("river.png");

Log.d(TAG, "File available:" +afd.getLength());

InputStream is=afd.createInputStream();

Bitmap bm=BitmapFactory.decodeStream(is);

is.close();

afd.close();

imageview.setImageBitmap(bm);

}catch(IOException e) {

e.printStackTrace();

}

将一张图片以AssetFileDescriptor的形式读取出来,并转换成Bitmap显示在ImageView上。

3.assets剖析

应用程序中每一个Activity组件都关联有一个ContextImpl对象,这个ContextImpl对象就是用来描述Activity组件的运行上下文环境的。Activity组件继承自Context类,而ContextImpl同样也继承自Context类。在Activity中调用的大部分成员函数都会转发给与它关联的一个ContextImpl对象的对应成员函数来处理,其中就包括用来访问应用程序资源的两个成员函数getResources()和getAssets()。

ContextImpl类的成员函数getResources()返回的是一个Resources对象,有了这个Resources对象之后,就可以通过资源ID来访问那些被编译过的应用程序资源了。

ContextImpl类的成员函数getAssets()返回的是一个AssetManager对象,有了这个AssetManager对象之后,就可以通过文件名来访问那些被编译过或者没有被编译过的应用程序资源文件了。事实上,Resources类也是通过AssetManager类来访问那些被编译过的应用程序资源文件的,不过在访问之前,它会先根据资源ID查找得到对应的资源文件名。

在Android系统中,一个进程可以同时加载多个应用程序,也就是可以同时加载多个APK文件。每一个APK文件在进程中都对应有一个全局的Resourses对象以及一个全局的AssetManager对象。其中这个全局的Resourses对象保存在ContextImpl对象的mResources成员变量中,而这个全局的AssetManager对象保存在这个全局的Resourses对象的成员变量mAssets中。

ContextImpl、Resources和AssetManager的关系图:

Resources类有一个成员函数getAssets(),通过它就可以获得保存在Resources类的成员变量mAssets中的AssetManager,例如ContextImpl类的成员函数getAssets()就是通过调用其成员变量mResources所指向的一个Resources对象的成员函数getAssets()来获得一个可以用来访问应用程序的非编译资源文件的AssetManager。

Android应用程序除了要访问自己的资源外,还需要访问系统的资源。系统的资源打包在/system/framework/framework-res.apk文件中,它在应用程序进程中是通过一个单独的Resources对象和一个单独的AssetManager对象来管理的。这个单独的Resources对象就保存在Resources类的静态成员变量mSystem中,可以通过Resources类的静态成员函数getSystem()获得这个Resources对象,而这个单独的AssetManager对象就保存在AssetManager类的静态成员变量sSystem中,同样可以通过AssetManager类的静态成员函数getSystem()获得这个AssetManager对象。

每一个Activity组件在进程的加载过程中,都会创建一个对应的ContextImpl,并且调用这个ContextImpl的init()方法来初始化Activity组件运行的上下文环境,其中就包括创建用来访问应用程序资源的Resources对象和AssetManager对象:

ContextImpl.init()方法:

class ContextImpl extends Context {

LoadedApk mPackageInfo;

private Resources mResources;

...…

final void init(LoadedApk packageInfo,  IBinder activityToken, ActivityThread mainThread) {

init(packageInfo, activityToken, mainThread, null);

}

final void init(LoadedApk packageInfo,  IBinder activityToken, ActivityThread mainThread,  Resources container) {

mPackageInfo = packageInfo;

mResources = mPackageInfo.getResources( mainThread);

......

}

......

}

参数packageInfo指向的是一个LoadedApk对象,这个LoadedApk对象描述的是当前正在启动的Activity组所属的Apk。用来访问应用程序资源的Resources对象是通过调用参数packageInfo所指向的是一个LoadedApk对象的成员函数getResources来创建的。这个Resources对象创建完成之后,就会保存在ContextImpl类的成员变量mResources中。

LoadedApk.getResources()方法:

final class LoadedApk {

private final String mResDir;

Resources mResources;

......

public Resources getResources(ActivityThread mainThread) {

if (mResources == null) {

mResources = mainThread.getTopLevelResources(mResDir, this);

}

return mResources;

}

......

}

参数mainThread指向了一个ActivityThread对象,这个ActivityThread对象描述的是当前正在运行的应用程序进程。在调用ActivityThread类的getTopLevelResources()方法获得一个Resources对象的时候,需要指定要获取的Resources对象所对应的Apk文件路径,这个Apk文件路径就保存在LoadedApk类的成员变量mResDir中。例如,假设要获取的Resources对象是用来访问系统自带的音乐播放器的资源的,那么对应的Apk文件路径就为/system/app/Music.apk。

ActivityThread.getTopLevelResources()方法:

public final class ActivityThread {

......

final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources  = new HashMap<ResourcesKey, WeakReference<Resources> >();

Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {

ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);

Resources r;

synchronized (mPackages) {

......

WeakReference<Resources> wr = mActiveResources.get(key);

r = wr != null ? wr.get() : null;

......

if(r != null && r.getAssets().isUpToDate()){

......

return r;

}

}

......

AssetManager assets = new AssetManager();

if (assets.addAssetPath(resDir) == 0) {

return null;

}

......

r = new Resources(assets, metrics, getConfiguration(), compInfo);

......

synchronized (mPackages) {

WeakReference<Resources> wr = mActiveResources.get(key);

Resources existing = wr != null ? wr.get() : null;

if (existing != null && existing.getAssets().isUpToDate()) {

r.getAssets().close();

return existing;

}

mActiveResources.put(key, new WeakReference<Resources>(r));

return r;

}

}

......

}

ActivityThread类的成员变量mActiveResources指向的是一个HashMap。这个HashMap用来维护在当前应用程序进程中加载的每一个Apk文件及其对应的Resources对象的对应关系。也就是说,给定一个Apk文件路径,ActivityThread类的成员函数getTopLevelResources可以在成员变量mActiveResources中检查是否存在一个对应的Resources对象。如果不存在,那么就会新建一个,并且保存在ActivityThread类的成员变量mActiveResources中。

参数resDir即为要获取其对应的Resources对象的Apk文件路径,ActivityThread类的成员函数getTopLevelResources首先根据它来创建一个ResourcesKey对象,然后再以这个ResourcesKey对象在ActivityThread类的成员变量mActiveResources中检查是否存在一个Resources对象。如果存在,并且这个Resources对象里面包含的资源文件没有过时,即调用这个Resources对象的成员函数getAssets所获得一个AssetManager对象的成员函数isUpToDate的返回值等于true,那么ActivityThread类的成员函数getTopLevelResources就可以将该Resources对象返回给调用者了。

如果不存在与参数resDir对应的Resources对象,或者存在这个Resources对象,但是存在的这个Resources对象是过时的,那么ActivityThread类的成员函数getTopLevelResources就会新创建一个AssetManager对象,并且调用这个新创建的AssetManager对象的成员函数addAssetPath来将参数resDir所描述的Apk文件路径作为它的资源目录。

创建了一个新的AssetManager对象,还需要这个AssetManager对象来创建一个新的Resources对象。这个新创建的Resources对象需要以前面所创建的ResourcesKey对象为键值缓存在ActivityThread类的成员变量mActiveResources所描述的一个HashMap中,以便以后可以获取回来使用。不过,这个新创建的Resources对象在缓存到ActivityThread类的成员变量mActiveResources所描述的一个HashMap去之前,需要再次检查该HashMap是否已经存在一个对应的Resources对象了,这是因为当前线程在创建新的AssetManager对象和Resources对象的过程中,可能有其它线程抢先一步创建了与参数resDir对应的Resources对象,并且将该Resources对象保存到该HashMap中去了。

如果没有其它线程抢先创建一个与参数resDir对应的Resources对象,或者其它线程抢先创建出来的Resources对象是过时的,那么就会将前面创建的Resources对象缓存到成员变量mActiveResources所描述的一个HashMap中去,并且将前面创建的Resources对象返回给调用者。

Android assets相关推荐

  1. android unzip file,Unzip File in Android Assets

    可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试): 问题: I put a zip file in the android assets. How do ...

  2. assets android 作用,Android assets文件夹之位置放置和作用

    Android 的assets文件夹的放置位置,Eclipse创建项目时就生成了的,Android Studio则不太一样,AS可以包含几种方式, 1:可以在build.gradle文件下配置,加如下 ...

  3. unity3d android assets,Unity3D之Android同步方法读取streamingAssets(八十八)

    版本unity5.3.3 android 小米pad1 streamingAssets 这个目录在IOS下是可以同步读取的,但是在Android下必须用www来异步读取..这就很恶心了-所以最近我就在 ...

  4. Android /assets

    Android提供一个/assets目录,可以将要包含在包中的文件放在这里.这个目录与/res具有相同的级别,也就是说它包含在/res子目录中. /assets中的文件不会在R.JAVA中生成资源ID ...

  5. android assets资源使用——深入浅出

    文章目录 0 前置知识点 1 android有两种资源文件: 2 通过AssetManager类访问asset中的资源 2.1 概述 2.2 常用方法 2.3 应用实例 1.访问assets目录下的资 ...

  6. android assets目录下资源,Android之assets资源目录的各种操作

    既然是要对assets资源目录操作.首先来解释下assets是啥? Android 中资源分为两种, ①.第一种是res下可编译的资源文件,这种资源文件系统会在R.java里面自动生成该资源文件的ID ...

  7. android assets绝对路径,Android 获取assets的绝对路径

    第一种方法: String path = "file:///android_asset/文件名"; 第二种方法: InputStream abpath = getClass().g ...

  8. android assets 在哪里,轻读一下 Android 应用开发中的 assets 目录

    2019-08-07 关键字:APK预置文件.预置配置文件.res,raw与assets的区别 在Android的应用开发中,难免会遇到外部文件的预置需求.例如图像.音视频.配置文件.字体等等.对于图 ...

  9. android+assets+在哪,我在哪里将’assets’文件夹放在Android Studio中?

    我对assets文件夹感到困惑. 它不是在Android Studio中自动创建的,几乎所有论坛都讨论了Eclipse. 如何在Android Studio中配置Assets目录? 由于Android ...

最新文章

  1. Servlet运行原理以及生命周期
  2. 啥不懂也能动手搭建属于自己的博客网站
  3. 区块链技术开发三个优势
  4. js 正则之检测素数
  5. CSP认证201509-3 模板生成系统[C++题解]:字符串处理、模拟、哈希表、引号里面有空格的字符串怎么读入
  6. ngRx 官方示例分析 - 4.pages
  7. 轻量易用的微信Sdk发布——Magicodes.Wx.Sdk
  8. java飞鸽传书_feige 飞鸽传书源代码java 实现不错的联系网络编程的资料飞鸽传书的GUI(java实现) - 下载 - 搜珍网...
  9. 使用 IDEA 创建 Scala 工程
  10. MPC5744P-时钟模块
  11. ubuntu18.04修改ip地址
  12. 狗年出生的宝宝取名都有哪些注意事项呢?起名真不是简单事
  13. 机器人聊天软件c#_用C#开发MSN插件程序(比如聊天机器人)
  14. html5做九九乘法表,利用JavaScript制作九九乘法表实例教程
  15. 人生短暂,持之以恒地做一件事情就会成功(每当烦躁心急如焚的时候就读读这篇文章吧)--转自孵化恐龙蛋
  16. 蓝牙耳机一个响一个不响怎么办
  17. 程序员创业:小程序开发费用报价表,包含项目工期和费用明细
  18. JS 实现数字滚动变化效果
  19. JavaScript——调试的使用
  20. 如何实现双因素认证?

热门文章

  1. Spring注解开发(Springboot源码必备前置知识)
  2. 安全(六种核心安全机制-加密、密钥、签名与证书)
  3. 依赖注入(DI)和Ninject
  4. python colorbar范围_python-在matplotlib中设置colorbar范围
  5. 【整理】【精华】【实用】常用代码片段
  6. javascript switch循环
  7. Hikari连接池1--初始化连接池
  8. 解决生成文件中文乱码(阿里代码规范插件导出html乱码)
  9. 【中级软考】软件开发生命周期模型 瀑布模型、增量模型、原型模型、螺旋模型、喷泉模型、RUP(Rational Unified Process 统一软件开发过程)、敏捷开发(开发方法,不是周期模型)
  10. 在 rails 3 环境中是用 生产环境 nginx + passenger 出现 css 文件无法编译的错误