Android assets

news2024/10/7 8:28:14

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目录下保存就好了。

 

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的关系图:

2aba9942382248fd863c5afbf43ad8b6.png

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对象返回给调用者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库mysql操作语言, DDL,DML,DQL

文章目录一. 数据库1. 数据库基本概念2. 数据库管理系统3. 数据库与表的概念二. 连接数据库的方式三. 如何操作DBMSSQL语句分类1. DDL 数据定义语言查看DBMS中已有的数据库数据库相关操作新建一个数据库查看数据库信息删除数据库使用一个数据库(切换一个数据库)表相关操作创建表…

HOOPS/MVO技术概述

更多参见&#xff1a;HOOPS学习笔记 MVO 1.引言 HOOPS/MVO是一个C类库&#xff0c;位于HOOPS 3D图形系统&#xff08;HOOPS/3DGS&#xff09;之上。它有一个模型/视图/操作员架构&#xff0c;封装了各种HOOPS/3DGS数据结构和概念&#xff0c;并提供了一系列通用应用程序级逻辑…

【无人机】基于粒子群优化干扰受限下无人机群辅助网络附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Xception --tensorflow2.x

简介 Xception和SqueezeNet一样&#xff0c;是一种降低参数量的轻量级神经网络&#xff0c;它主要使用了 深度分离卷积&#xff08;Depthwise separable convolution)结构&#xff0c;该结构替换了原来的Inception中的多尺寸卷积结构。这里需要弄清深度分离卷积&#xff08;D…

【创建型设计模式-单例模式】一文搞懂单例模式的使用场景及代码实现的7种方式

1.什么是单例模式 在了解单例模式前&#xff0c;我们先来看一下它的定义&#xff1a; 确保一个类只有一个实例&#xff0c;而且自行实例化并且自行向整个系统提供这个实例&#xff0c;这个类称为单例类&#xff0c;它提供全局访问的方法&#xff0c; 单例模式是一种对象的创建型…

微型计算机原理速通期末复习

文章目录微机基础原码、反码、补码、移码溢出实数型功能结构8086/8088内部结构80286内部结构80386/80486内部结构标志寄存器FLAGS寄存器阵列段寄存器寻址标志寄存器EFLAGS分段结构数据寻址方式立即寻址直接寻址寄存器寻址寄存器间接寻址寄存器相对寻址基址-变址寻址基址-变址-相…

Solidity vs. Vyper:不同的智能合约语言的优缺点

本文探讨以下问题&#xff1a;哪种智能合约语言更有优势&#xff0c;Solidity 还是 Vyper&#xff1f;最近&#xff0c;关于哪种是“最好的”智能合约语言存在很多争论&#xff0c;当然了&#xff0c;每一种语言都有它的支持者。 这篇文章是为了回答这场辩论最根本的问题&…

磨金石教育摄影技能干货分享|中国风摄影大师——郎静山

说到中国风摄影&#xff0c;你想到的画面是什么样子的&#xff1f;故宫、长城、苏州园林、大红灯笼高高挂&#xff0c;反正离不开传承了千八百年的古建筑。仿佛没有了这些历史古董的元素就没有中国味道似的。 其实中国风&#xff0c;其内核应该是传统的审美观念和哲学思想。中…

【雷丰阳-谷粒商城 】课程概述

持续学习&持续更新中… 学习态度&#xff1a;守破离 【雷丰阳-谷粒商城 】课程概述该电商项目与其它项目的区别项目简介项目背景电商模式谷粒商城项目技术&特色项目前置要求谷粒商城-微服务架构图谷粒商城-微服务划分图参考该电商项目与其它项目的区别 互联网大型项目…

深入linux内核架构--内存管理

【推荐阅读】 代码大佬的【Linux内核开发笔记】分享&#xff0c;前人栽树后人乘凉&#xff01; 一文了解Linux内核的Oops 一篇长文叙述Linux内核虚拟地址空间的基本概括 路由选择协议——RIP协议 深入理解Intel CPU体系结构【值得收藏&#xff01;】 内存体系结构 1. UM…

银行测试人员谈测试需求

今天呢&#xff0c;想用故事说话&#xff0c;先看看啥叫用户需求挖掘。其实看完故事之后&#xff0c;我自己颇为震撼&#xff0c;请看。 故事一&#xff1a; 100多年前&#xff0c;福特公司的创始人亨利福特先生到处跑去问客户&#xff1a;“您需要一个什么样的更好的交通工具…

loganalyzer 展示数据库中的日志

1 实验目标&#xff1a; 利用rsyslog日志服务&#xff0c;将收集的日志记录于MySQL中&#xff0c;通过loganalyzer 展示数据库中的日志 2 环境准备 三台主机&#xff1a; 一台日志服务器&#xff0c;利用上一个案例实现&#xff0c;IP&#xff1a;192.168.100.100一台数据库…

【Java八股文总结】之数据结构

文章目录数据结构一、概念1、时间复杂度与空间复杂度2、常见算法时间复杂度3、Comparable二、常见的排序算法1、直接插入排序2、希尔排序3、选择排序4、堆排序5、冒泡排序6、快速排序7、归并排序8、二分查找算法Q&#xff1a;什么时候需要结束呢&#xff1f;三、线性表1、概念2…

使用 Footprint Analytics, 快速搭建区块链数据应用

Nov 2022, danielfootprint.network 如果你有一个处理 NFTs 或区块链的网站或应用程序&#xff0c;你可以在你的平台上直接向用户展示数据&#xff0c;以保持他们在网站或者应用内的参与&#xff0c;而不是链接以及跳出到其他网站。 对于任何区块链应用或者媒体、信息网站来说…

秦皇岛科学选育新品种 国稻种芯·中国水稻节:河北秸秆变肥料

秦皇岛科学选育新品种 国稻种芯中国水稻节&#xff1a;河北秸秆变肥料 秦皇岛新闻网 记者李妍 冀时客户端报道&#xff08;河北台 张志刚 米弘钊 赵永鑫 通讯员 赵力楠&#xff09; 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯…

无线通信技术概览

电生磁&#xff0c;磁生电 电场和磁场的关系&#xff0c;简而言之就是&#xff1a;变化的电场产生磁场&#xff0c;变化的磁场产生电场。 电荷的定向移动产生电流&#xff0c;电荷本身产生电场。电流是移动的电场。静止的电荷产生静止的电场&#xff0c;运动的电荷产生运动的电…

java实现阿里云rocketMQ消息的发送与消费(http协议sdk)

目录一、准备工作二、代码实现1.添加依赖2.创建一个常量类存放公共参数3.调用HTTP协议的SDK 发送普通消息4.调用HTTP协议的SDK 订阅普通消息三、配置main的日志输出级别四、测试效果五、完成代码一、准备工作 登录阿里云官网&#xff0c;先申请rocketMQ&#xff0c;再申请Topi…

一文带你了解PCB设计中的常用基本概念

本文将从初学者的角度出发&#xff0c;一文带你快速了解PCB设计中的常用基本概念&#xff1a;一、FR4板材FR-4就是玻璃纤维环氧树脂覆铜板&#xff0c;线路板中的一种基材&#xff0c;可以分为一般FR4板材和高TG FR4板材&#xff0c;Tg是玻璃转化温度&#xff0c;即熔点。电路板…

分享好玩的h5小游戏制作_为什么要做h5微信小游戏呢

近年来&#xff0c;市面上一直流行各种h5游戏&#xff0c;例如投票、答题、刮刮乐、大转盘等等等等&#xff0c;而且我在各种营销场景下经常看到它们的身影&#xff0c;是做促销&#xff0c;引流和宣传的神器之一&#xff01; 那么&#xff0c;怎么做好玩的h5游戏&#xff1f;还…

DIXml v5.21.0 for Delphi 11

DIXml v5.21.0 for Delphi 11 DIXml是一个嵌入式XML、XSLT&#xff0c;也是Delphi的EXSLT处理库(Embarcadero//CodeGear/Borland)。它构建在libxml2、libxslt和libexslt库上&#xff0c;但不需要更多的DLL或其他外部文件。 DIXml很容易成为Delphi中功能最齐全的XML和XSLT替代品…