在研究如何遍历Assets下的文件时,我得到了一点奇怪的结果 - 看到了一些本不属于当前应用的文件结构。
首先贴一下遍历代码
public static void printAssets(Context c) {
AssetManager am = c.getAssets();
printAssetsFiles(am, "", "");
}
public static void printAssetsFiles(AssetManager am, String parent, String current, String indent) {
if (!TextUtils.isEmpty(current)) {
System.out.println(indent + current);
indent += "\t";
}
// 列出子文件
String[] files;
String currentParent;
try {
if (TextUtils.isEmpty(parent)) {
currentParent = current;
} else {
currentParent = parent + "/" + current;
}
files = am.list(currentParent);
} catch (IOException e1) {
return;
}
if (files != null && files.length > 0) {
for (String f : files) {
printAssetsFiles(am, currentParent, f, indent);
}
}
}
下面是在我手机上跑出来的结果,运行环境是小米4,Android4.4.4
MyFile1.txt
MyFile2.txt
MyFolder1
MyFile1.txt
MySubFolder1
MyFile1-1.txt
MyFolder3
MyFile2.txt
MyFolder4
MySubFolder4
MyFile1.txt
device_features
H2X_I18N.xml
HM2013022.xml
HM2013023.xml
HM2014011.xml
HM2014112.xml
HM2014501.xml
HM2014811.xml
HM2014812.xml
HM2014813.xml
HM2014817.xml
HM2014818.xml
HM2014819.xml
HM2014821.xml
aries.xml
armani.xml
cancro_MI3.xml
cancro_MI4.xml
dior.xml
ferrari.xml
gucci.xml
hammerhead.xml
lcsh92_wet_jb9.xml
lcsh92_wet_tdd.xml
leo.xml
lte26007.xml
mocha.xml
pisces.xml
taurus.xml
virgo.xml
huangli.idf
images
android-logo-mask.png
android-logo-shine.png
license
br
pt_BR
eula.html
privacy.html
default
en_US
copyright.html
eula.html
mibilicense.html
miclouduseragreement.htm
privacy.html
zh_CN
copyright.html
eula.html
mibilicense.html
miclouduseragreement.htm
privacy.html
zh_TW
copyright.html
eula.html
miclouduseragreement.htm
privacy.html
sg
en_US
privacy.html
pinyinindex.idf
sounds
bootanim0.raw
bootanim1.raw
telocation.idf
webkit
android-weberror.png
hyph_en_US.dic
incognito_mode_start_page.html
missingImage.png
nullPlugin.png
play.png
textAreaResizeCorner.png
togglePlugin.png
youtube.html
youtube.png
东西很多,但是在我应用的Assets下的文件夹结构是这样的:MyFile1.txt
MyFile2.txt
MyFolder1
MyFile1.txt
MySubFolder1
MyFile1-1.txt
MyFolder3
MyFile2.txt
MyFolder4
MySubFolder4
MyFile1.txt
在这里我们会发现,输出列表中包含大量的文件,这些文件都不是我们应用中的。
(在这里说两句题外话,输出列表中是没有空目录的,是因为apk在打包的时候把空目录过滤了,大家可以用压缩软件打开看看)
那么多出来的文件都是从哪里来的呢?我们去扒一扒AssetManager的源码,先从list方法开始:
/**
* Return a String array of all the assets at the given path.
*
* @param path A relative path within the assets, i.e., "docs/home.html".
*
* @return 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().
*
* @see #open
*/
public native final String[] list(String path)
throws IOException;
这是个本地的方法,虽然没有源码,但是从注释中也能看出来,“all the assets”说明这里不是只有一个Assets目录的,那么除了我们自己的apk,肯定不能是其他人的apk啊,那就只能是系统的Assets了。
再翻代码,发现AssetManager中有个静态的实例sSystem,看起来这就是系统的Assets了。这个类中有一个getSystem方法,是获取该实例的。这是个隐藏方法,我们可以用反射获取:
Method getSystem = AssetManager.class.getDeclaredMethod("getSystem");
AssetManager am = (AssetManager) getSystem.invoke(null);
遍历这个AssetManager的目录,可以得到上面多出来的文件列表。至于这个Assets文件的位置在哪里,我没有找到,有兴趣的读者可以找找。
另外,如果apk Assets下的文件路径和系统中的文件路径相同,那么在读取的时候,apk自己的文件会覆盖系统文件,所以不需要担心此问题。
最后,值得一提的是,如果我么使用如下参数调用上面的方法:
printAssetsFiles(am, "/", "");
得到的将是apk压缩文件的根目录结构如下:
AndroidManifest.xml
META-INF
assets
classes.dex
lib
res
resources.arsc
此时使用am.open打开文件,只能得到异常。不过对于文件,比如AndroidManifest.xml,可以使用AssetManager的隐藏api方法openNonAsset打开,但是对于目录就无能为力了,当然,API是不建议用户用AssetManager打开这个文件的(所以文档中称呼这些文件为NonAsset文件)
CyanFlxy原创,转载请注明出处。