背景知识介绍

与其他平台的应用程序一样,Android中的应用程序也会使用各种资源,比如图片,字串等,会把它们放入源码的相应文件夹下面,如/res/drawable, /res/xml, /res/values/, /res/raw, /res/layout和/assets。Android也支持并鼓励开发者把UI相关的布局和元素,用XML资源来实现。总结起来,Android中支持的资源有:
  • 颜色值 /res/values 以resources为Root的XML文件,定义形式为<color name>value</color>
  • 字串 /res/values 以resources为Root的XML文件<string name>value</string>
  • 图片 /res/drawable 直接放入,支持9 Patch可自由拉伸
  • 图片的颜色 /res/values 以resources为Root的XML文件,定义形式为<drawable name>value</drawable>
  • 单位资源 /res/values 以resources为Root的XML文件<dimen name>value</dimen>
  • 菜单 /res/menu 以menuo为root的XML文件
  • 布局 /res/layout 这个就是GUI的布局和元素
  • 风格和主题 /res/values 以resources为Root的XML文件<style name>value</style>
  • 动画 /res/anim 有二种:一个是帧动画(frame animation),也就是连续变换图片以animation-list为Root的XML文件;另外一种就是补间动画(tweened animation),它对应于API中的Animation和AnimationSet,有translate、scale、rotate、alpha四种,以set为root来定义,这个set就相当于AnimationSet
再说下目录:
  • /res/anim 用于存放动画
  • /res/drawable 存放图片,或等同于图片的资源如shape,或selector
  • /res/menu 存放Menu
  • /res/values 存放修饰性资源:字串,颜色,单位,风格和主题
  • /res/layout 存放UI布局和元素
  • /res/raw 存放运行时想使用的原始文件
  • /assets 存放运行时想使用的原始文件
除了原始文件目录/res/raw和/assets以外,其他的资源在编译的时候都会被第三方软件aapt进行处理,一个是把图片和XML文件进行处理,例如把XML编译成为二进制形式;另外处理的目的就是生成R.java文件,这个文件是访问资源时必须要用到的。
/res目录下面的所有文件都会映射到R.java文件中,以整数Id的形式被标识,相同类型的资源被一个内部类来封装,一个R.java的文件类似于这样:

[java] view plain copy print ?
  1. /*AUTO-GENERATEDFILE.DONOTMODIFY.
  2. *
  3. *Thisclasswasautomaticallygeneratedbythe
  4. *aapttoolfromtheresourcedataitfound.It
  5. *shouldnotbemodifiedbyhand.
  6. */
  7. packagecom.android.explorer;
  8. publicfinalclassR{
  9. publicstaticfinalclassattr{
  10. }
  11. publicstaticfinalclassdrawable{
  12. publicstaticfinalinticon=0x7f020000;
  13. }
  14. publicstaticfinalclassid{
  15. publicstaticfinalintaction=0x7f060004;
  16. publicstaticfinalintdescription_panel=0x7f060001;
  17. publicstaticfinalintfileinfo=0x7f060003;
  18. publicstaticfinalintfilename=0x7f060002;
  19. publicstaticfinalintlinearlayout_test_1=0x7f060005;
  20. publicstaticfinalintlinearlayout_test_2=0x7f060006;
  21. publicstaticfinalintlinearlayout_test_3=0x7f060007;
  22. publicstaticfinalintthumbnail=0x7f060000;
  23. }
  24. publicstaticfinalclasslayout{
  25. publicstaticfinalintfileant_list_item=0x7f030000;
  26. publicstaticfinalintlinearlayout_test=0x7f030001;
  27. }
  28. publicstaticfinalclassraw{
  29. publicstaticfinalintandroidmanifest=0x7f040000;
  30. }
  31. publicstaticfinalclassstring{
  32. publicstaticfinalintapp_name=0x7f050001;
  33. publicstaticfinalinthello=0x7f050000;
  34. }
  35. }
从这个R.java就可看出在/res中定义或提供资源时的注意事项:
1. 同一个类型,或同一文件夹下面的资源不可以使用相同的文件名,也就是说不能用文件扩展名来区别不同的文件,因为R.java中只保留资源的文件名而不管扩展名,所以如果有二个图片一个是icon.png另一个是icon.jpg,那么在R.java中只会有一个R.drawable.icon。另外一个则会无法访问到。
2. 资源文件的名字必须符合Java变量的命名规则,且不能有大写,只能是'[a-z][0-9]._',否则会有编译错误,因为R.java中的变量Id要与资源中的文件一一对应,也就是说用资源文件名来作为Id的变量名,所以一定要符合Java变量的命名规则,另外它还不能有大写。
3. 除了SDK支持的folder外,不能再有子Folder,虽不会有编译错误,但是子Folder会被完全忽略,如在/res/layout下在建一个子Folder activity(/res/layout/acitivity/, 那么你在生成的R.java中是看不到activity和其内的内容的。
4. 对于资源文件的大小有限制,最好不要让单个文件大于1M,这是SDK文档说明的限制,但具体的我没有进行试验(据说2.2版本以后的可支持到10M,不知道是真的还是假的)
5. 所有/res下面的资源都能通过Resources()并提供Id来访问。

使用原始资源

对于大多数资源在编译时会对文件内容进行特殊处理,以方便Apk在运行时访问。 如果想要运行时使用未经处理的原始资源,可以把资源文件放在/res/raw和/assets目录下面,这二个目录的主要区别在于:
1. /res/raw中的文件会被映射到R.java中
虽然/res/raw中的文件不会被aapt处理成为二进制,但是它的文件还是被映射到R.java中,以方便以资源Id形式来访问
2. 子目录结构
如上面所述,/res/raw中虽可以有子目录,但是在程序运行时是无法访问到的,因为/res下面的所有非法子目录在R.java中都是看不到的。而且这些子目录和文件都不会被编译进入Apk中,解压Apk文件后也看不到/res/raw下面去找了。
而/assets是允许有子目录的,并且完全可以访问到,并且会被打包进Apk,解压Apk后,这些文件仍然存在并且与源码包中的一样。
3. 访问方式
/res/raw下面的文件(子文件夹是访问不到的了)的访问方式是通过Resources,并且必须提供资源的Id
[java] view plain copy print ?
  1. InputStreamin=Context.getResources().openRawResource(R.id.filename);
所以为什么子文件夹无法访问,因为没有Id啊。
而/assets则要通过AssetManager来访问。下面着重讲解如何访问/assets下面的资源文件。

通过AssetManager来访问/assets下面的原始资源文件

1. 文件的读取方式
用AssetManager.open(String filename)来打开一个文件,这是一组重载方法,还有其他参数可以设置打开模式等,可以参考文档
这里的filename是相对于/assets的路径,比如:

[java] view plain copy print ?
  1. InputStreamin=mAssetManager.open("hello.txt");//'/assets/hello.txt'
  2. InputStreamin2=mAssetManager.open("config/ui.txt");//'/assets/config/ui.txt'
2. 文件夹处理 --- 如何遍历/assets
可以看到如果想要访问/assets下面的文件,必须要知道文件名和相对于/assets的路径。所以,如果你不预先知道其下面有什么的时候又该如何处理呢?那就需要列出它下面所有的文件,然后再选取我们需要的,所以新的问题来了,如何列出/assets下面所有的文件呢?
AssetManager提供了一个列出/assets下某个路径下面的方法:

public finalString[]list(Stringpath)

Since:API Level 1

Return a String array of all the assets at the given path.

Parameters
path
A relative path within the assets, i.e., "docs/home.html".
Returns
  • String[] Array of strings, one for each asset. These file names are relative to 'path'. You can open the file by concatenating 'path' and a name in the returned string (via File) and passing that to open().
其实这个文档写的有问题,list()是列出一个文件夹下面的文件,所以应该传入一个文件夹路径而非文档中的"docs/home.html"。
还有一个最大的问题就是如何列出根目录/assets下面的内容,因为只有知道了根目录下面的东西,才能去相对的子目录去找东西,所以这是个必须最先解决的问题。
其实文档没有说的太明白这个方法到底如何使用,也就是说这个String参数到底如何传。猜想着根目录为/assets,所以尝试了以下:

[java] view plain copy print ?
  1. mAssetManager.list(".");//returnsarraysizeis0
  2. mAssetManager.list("/");//returns[AndroidManifest.xml,META-INF,assets,classes.dex,res,resources.arsc]//don'tworry,ucanseethesefilesthough,nowaytoaccessthem
  3. mAssetManager.list("/assets");//returnsarraysizeis0
  4. //Google了一下,找到了正解:
  5. mAssetManager.list("");//returnsstuffin/assets
然后再根据所列出的子项去递归遍历子文件,直到找到所有的文件为止。

常见的问题

1. 资源文件只能以InputStream方式来获取
如果想操作文件怎么办,如果想要用文件Uri怎么办。光靠API当然不行,它只给你InputStream,也就是说它是只读的。可行的办法就是读取文件然后写入一个临时文件中,再对临时文件进行想要的文件操作。可以在内部存储或外部存储上面用Context提供的接口来创建文件,详细的请参考这里。Java牛人可能想要用Java本身的能力:

[java] view plain copy print ?
  1. FileFile.createTempFile(Stringprefix,Stringsuffix);
  2. FileFile.createTempFile(Stringprefix,Stringsuffix,Filepath);
这也是可以的,但要考虑Android系统的特性,也就是说所写的路径是否有权限。比如对于第一个方法,用的是"java.io.tmpdir"这个在Android当中就是"/sdcard",所以当没有SD卡时这个方法必抛异常。
2. 所有资源文件都是只读的,运行时无法更改
因为,程序运行时是把Apk动态解析加载到内存中,也就是说,Apk是不会有变化的,它是无法被改变的(至于逆向工程来修改那是另外一回事,请参考这里)。
3. 所有的资源文件夹/res和/assets也都是只读的,不可写入

如上面所说,Apk是在编译后是无法再改变的了。

实例

下面是一个实例,可以递归式的遍历/assets下面所有的文件夹和文件

[java] view plain copy print ?
  1. packagecom.android.explorer;
  2. importjava.io.BufferedInputStream;
  3. importjava.io.BufferedOutputStream;
  4. importjava.io.File;
  5. importjava.io.FileNotFoundException;
  6. importjava.io.FileOutputStream;
  7. importjava.io.IOException;
  8. importjava.io.InputStream;
  9. importandroid.app.ListActivity;
  10. importandroid.content.Context;
  11. importandroid.content.Intent;
  12. importandroid.content.res.AssetManager;
  13. importandroid.net.Uri;
  14. importandroid.os.Bundle;
  15. importandroid.os.Environment;
  16. importandroid.text.TextUtils;
  17. importandroid.util.Log;
  18. importandroid.view.LayoutInflater;
  19. importandroid.view.View;
  20. importandroid.view.ViewGroup;
  21. importandroid.webkit.MimeTypeMap;
  22. importandroid.widget.BaseAdapter;
  23. importandroid.widget.ImageButton;
  24. importandroid.widget.LinearLayout;
  25. importandroid.widget.TextView;
  26. /*
  27. *Exploreallstuffin/assetsandperformactionsspecifiedbyusers.
  28. */
  29. publicclassFileAntActivityextendsListActivity{
  30. privatestaticfinalStringTAG="FileAntActivity";
  31. privateAssetManagermAssetManager;
  32. privatestaticfinalStringEXTRA_CURRENT_DIRECTORY="current_directory";
  33. privatestaticfinalStringEXTRA_PARENT="parent_directory";
  34. publicstaticfinalStringFILEANT_VIEW="com.android.fileant.VIEW";
  35. @Override
  36. publicvoidonCreate(BundlesavedInstanceState){
  37. super.onCreate(savedInstanceState);
  38. Intentintent=getIntent();
  39. Stringcurrent=null;
  40. Stringparent=null;
  41. if(intent!=null&&intent.hasExtra(EXTRA_CURRENT_DIRECTORY)){
  42. current=intent.getStringExtra(EXTRA_CURRENT_DIRECTORY);
  43. }
  44. if(current==null){
  45. current="";
  46. }
  47. if(intent!=null&&intent.hasExtra(EXTRA_PARENT)){
  48. parent=intent.getStringExtra(EXTRA_PARENT);
  49. }
  50. if(parent==null){
  51. parent="";
  52. }
  53. mAssetManager=getAssets();
  54. if(TextUtils.isEmpty(parent)){
  55. setTitle("/assets");
  56. }else{
  57. setTitle(parent);
  58. }
  59. try{
  60. //Listallthestuffin/assets
  61. if(!TextUtils.isEmpty(parent)){
  62. current=parent+File.separator+current;
  63. }
  64. Log.e(TAG,"current:'"+current+"'");
  65. String[]stuff=mAssetManager.list(current);
  66. setListAdapter(newFileAntAdapter(this,stuff,current));
  67. }catch(IOExceptione){
  68. e.printStackTrace();
  69. }
  70. }
  71. privateclassFileAntAdapterextendsBaseAdapter{
  72. privateContextmContext;
  73. privateString[]mEntries;
  74. privateStringmParentDirectory;
  75. publicFileAntAdapter(Contextcontext,String[]data,Stringparent){
  76. mContext=context;
  77. this.mEntries=data;
  78. mParentDirectory=parent;
  79. }
  80. publicintgetCount(){
  81. returnmEntries.length;
  82. }
  83. publicObjectgetItem(intposition){
  84. returnmEntries[position];
  85. }
  86. publiclonggetItemId(intposition){
  87. return(long)position;
  88. }
  89. publicViewgetView(finalintposition,Viewitem,ViewGroupparent){
  90. LayoutInflaterfactory=LayoutInflater.from(mContext);
  91. if(item==null){
  92. item=factory.inflate(R.layout.fileant_list_item,null);
  93. TextViewfilename=(TextView)item.findViewById(R.id.filename);
  94. TextViewfileinfo=(TextView)item.findViewById(R.id.fileinfo);
  95. ImageButtonaction=(ImageButton)item.findViewById(R.id.action);
  96. finalStringentry=mEntries[position];
  97. filename.setText(entry);
  98. booleanisDir=isDirectory(entry);
  99. if(isDir){
  100. fileinfo.setText("Clicktoviewfolder");
  101. action.setVisibility(View.GONE);
  102. item.setClickable(true);
  103. item.setOnClickListener(newView.OnClickListener(){
  104. publicvoidonClick(Viewview){
  105. Intentintent=newIntent(FILEANT_VIEW);
  106. intent.putExtra(EXTRA_CURRENT_DIRECTORY,entry);
  107. intent.putExtra(EXTRA_PARENT,mParentDirectory);
  108. startActivity(intent);
  109. }
  110. });
  111. }else{
  112. finalStringtype=
  113. MimeTypeMap.getSingleton().getMimeTypeFromExtension(getExtension(entry));
  114. fileinfo.setText(type);
  115. item.setClickable(false);
  116. action.setOnClickListener(newView.OnClickListener(){
  117. publicvoidonClick(Viewview){
  118. Stringfilepath=entry;
  119. if(!TextUtils.isEmpty(mParentDirectory)){
  120. filepath=mParentDirectory+File.separator+filepath;
  121. }
  122. BufferedInputStreamin=newBufferedInputStream(mManager.open(filepath));
  123. //Dowhateveryoulikewiththisinputstream
  124. }
  125. });
  126. }
  127. }
  128. returnitem;
  129. }
  130. }
  131. /**
  132. *TestWhetheranentryisafileordirectorybasedontherule:
  133. *File:hasextension*.*,orstartswith".",whichisahiddenfilesinUnix/Linux,
  134. *otherwise,itisadirectory
  135. *@paramfilename
  136. *@return
  137. */
  138. privatebooleanisDirectory(Stringfilename){
  139. return!(filename.startsWith(".")||(filename.lastIndexOf(".")!=-1));
  140. }
  141. privateStringgetExtension(Stringfilename){
  142. intindex=filename.lastIndexOf(".");
  143. if(index==-1){
  144. return"";
  145. }
  146. returnfilename.substring(index+1,filename.length()).toLowerCase();
  147. }
  148. }

更多相关文章

  1. Android的多媒体框架Opencore代码阅读
  2. Android初学者学习之Android(安卓)屏幕适配2
  3. Android开发5——文件读写
  4. android中自定义SeekBar
  5. VS2019中用xamarin开发Android,显示需要android sdk28的解决方法
  6. Android布局文件属性笔记
  7. Android之NDK开发
  8. 修改Android(安卓)Studio下的AVD的下载路径
  9. Android中对Log日志文件的分析

随机推荐

  1. 简化Android的UI开发
  2. 开源框架和开源项目
  3. android3.1开发环境配置
  4. Android的图形与图像处理之三 逐帧动画(Fr
  5. Android中声音的管理类AudioManager
  6. 转载:Android开源项目分享
  7. Android下自定义IP控件
  8. 使用Android平板编程,执行linux命令
  9. Android(安卓)ROM开发之预制GMS
  10. android的振动器Vibrator