https://github.com/xiandanin/AssetFile
如果想跳过直接看文档,可以拉到最底
前言
平时开发中经常会用到Assets,可以让我们把一些资源内置在应用里,但是它使用起来比较麻烦,比如要使用Assets里面的一个文件,需要这样:
1 2 3 4 5
| try { final InputStream stream = getAssets().open("test.jpg"); } catch (IOException e) { e.printStackTrace(); }
|
拿到的是InputStream,当需要复制到手机外部存储的时候,还得用FileOutputStream
输出到文件,如果需要获取文件夹下面的文件:
1 2 3 4 5
| try { final String[] list = getAssets().list("test"); } catch (IOException e) { e.printStackTrace(); }
|
这样获取到的只是文件夹下的文件名,如果要复制整个文件夹到手机目录至少要3步:
- 遍历;
- 拼接完整路径;
- 调用open输出到文件。
强迫症表示很难受,没有Java File的API那么方便,那只能撸一个轮子让它像File一样了。
下面的图是用AssetFile
做的Assets文件管理器
使用场景
先举一个我遇到的场景,在线滤镜和内置滤镜,在线滤镜需要先把配置文件下载到手机目录,内置滤镜就是放在Assets里的,为了方便管理,把两种滤镜都放到统一的目录,AssetFile先解决了一个复制文件夹的问题。
如果要获取下图的滤镜1.json,实际上是这样assetManager.open(filter/group/滤镜1/滤镜1.json)
,文件名和文件数量实际上是不受开发控制的,也不可能每次变化都再去修改一遍文件名或者代码,所以AssetFile将多层次文件夹的操作简单化了
AssetFile并没有省略那些流程,只是经过封装,让它使用起来变得更简单了
基本实现
首先先来实现一下基本功能,Java的叫File,那我们就叫AssetFile,先暂时存一些基本信息,路径和文件名
1 2 3 4
| public class AssetFile { private String assetPath; private String name; }
|
我们在用File的时候是传文件路径,AssetFile也一样,假设Assets根目录有一个test.jpg
文件
1 2 3 4 5 6 7 8 9 10 11
| public class AssetFile { private String assetPath; private String name; public AssetFile(String assetPath) { this.assetPath = assetPath == null ? "" : assetPath; //有/的话会去掉/ int index = assetPath.lastIndexOf(File.separatorChar); this.name = assetPath.substring(index + 1, assetPath.length()); } }
|
1
| new AssetFile("test.jpg")
|
文件API
基本信息也赋好值了,可以说已经有一个雏形了,现在添加一些常用的API,比如exists()
当调用AssetManager.list(assetPath)
的时候,如果找不到这个文件,会抛出IOException
,所以只要catch到就说明这个文件不存在。
1 2 3 4 5 6 7 8 9
| public boolean exists(AssetManager assetManager) { try { assetManager.list(assetPath); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
|
1 2
| AssetFile file = new AssetFile("test.jpg") boolean exists = file.exists(context.getAssets())
|
加入文件夹的支持
文件的API比较简单,要考虑文件夹就会有一些逻辑了
isDirectory()
现在我们加入一个isDirectory ()
,用来判断这个路径是否是文件夹,可以获取该路径下的子文件数组,来判断这个路径是不是一个文件夹,如果数组长度大于0说明是一个文件夹。
当然这个方法也有不准确的情况,比如你放了一个空文件夹在Assets里,但是为什么要放一个空文件夹在Assets里呢,所以这个方法还是可行的。
这里还有一个处理,assetManager.list()
是把子文件的路径都添加到数组里了,所以不能每次都这样取一次子文件数组,可以用一个变量来缓存它,当它有值的时候,就直接用变量的值了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class AssetFile { private Boolean directory;
public boolean isDirectory(AssetManager assetManager) { if (directory == null) { try { directory = assetManager.list(assetPath).length > 0; } catch (Exception e) { e.printStackTrace(); directory = false; } } return directory; } }
|
getParent()
还有一个常用的方法就是获取父文件夹,这个比较简单,通过分割路径字符串就可以实现。
比如A文件夹里放了B文件夹,B文件夹放了C文件,那路径其实就是A/B/C.jpg
,通过substring
从0
开始截取到最后的/
,截取后的A/B
就是父文件夹的路径了。
1 2 3 4 5 6 7 8
| public String getParent() { int index = assetPath.lastIndexOf(File.separatorChar); return assetPath.substring(0, index); }
public AssetFile getParentFile() { return new AssetFile(getParent()); }
|
listFiles()
既然有文件夹,就肯定会需要用到listFiles()
,assetManager.list()
提供的只是子文件的文件名称,所以需要把它拼接成完整的路径传给AssetFile
。
还可以添加一个AssetFileFilter
,return false
的就不添加到集合;了。当根目录的时候调用assetManager.list
会把系统自带的一些文件列出来,所以这里还实现了一个SystemAssetFileFilter
,用来过滤这些文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public List<AssetFile> listFiles(AssetManager assetManager) { return listFiles(assetManager, new SystemAssetFileFilter()); }
public List<AssetFile> listFiles(AssetManager assetManager, AssetFileFilter filter) { try { String newAssetPath = TextUtils.isEmpty(assetPath) ? "" : assetPath; //先获取子文件数组 String[] list = assetManager.list(newAssetPath); List<AssetFile> fileList = new ArrayList<>(); for (int i = 0; i < list.length; i++) { AssetFile file = new AssetFile(newAssetPath, list[i]); if (filter != null) { //如果有过滤器,返回true的才添加到AssetFile集合 if (filter.accept(file)) { fileList.add(file); } } else { //否则直接添加 fileList.add(file); } } return fileList; } catch (IOException e) { e.printStackTrace(); } return new ArrayList<>(); }
|
AssetsManager
为了更方便的使用,可以再扩展一个AssetsManager
,提供一些辅助性的方法,比如从Assets复制资源到手机目录,复制文件很简单,拿到流之后直接输出到文件,复制文件夹需要递归来达到复制多层文件夹的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| /** * 复制Asset文件夹和里面的文件到手机目录 * * @param assetSource * @param outputDir */ public static boolean copyAsset(AssetManager assetManager, AssetFile assetSource, File outputDir) { try { File outputFile = new File(outputDir, assetSource.getName());
String assetPath = assetSource.getAssetPath(); final String[] list = assetManager.list(assetPath); if (list.length <= 0) { //文件 copyAssetFile(assetManager, assetPath, outputFile); } else { //目录 if (!outputFile.exists()) { outputFile.mkdirs(); } for (String child : list) { copyAsset(assetManager, new AssetFile(assetPath, child), outputFile); } } return true; } catch (IOException e) { e.printStackTrace(); } return false; }
/** * 复制Asset文件到手机目录 * * @param assetPath * @param outputFile * @return */ public static boolean copyAssetFile(AssetManager assetManager, String assetPath, File outputFile) { try { InputStream is = assetManager.open(assetPath); int byteRead = 0; FileOutputStream fs = new FileOutputStream(outputFile); byte[] buffer = new byte[1024]; while ((byteRead = is.read(buffer)) != -1) { fs.write(buffer, 0, byteRead); } fs.close(); is.close(); return true; } catch (Exception e) { e.printStackTrace(); } return false; }
|
文档
Gradle引入
1
| implementation 'com.dyhdyh.io:asset-file:1.0.2'
|
AssetFile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| //根目录 AssetFile root = new AssetFile(); //根目录下的test.jpg AssetFile testFile = new AssetFile("test.jpg"); //test文件夹下的test.jpg - test/test.jpg AssetFile testFile = new AssetFile("test/test.jpg"); AssetFile testFile = new AssetFile("test", "test.jpg");
//获取完整路径 assetFile.getAssetPath();
//获取文件名称或目录名称 assetFile.getName();
//获取父级目录 assetFile.getParentFile();
//转换Uri assetFile.getUri();
//是否文件夹 assetFile.isDirectory(getAssets());
//是否根目录 assetFile.isRootDir();
//文件是否存在 assetFile.exists(getAssets());
//获取目录下的文件数组 assetFile.listFiles(getAssets());
|
AssetsManager
1 2 3 4 5 6 7
| //复制Assets里的test.jpg到手机根目录 AssetFile assetFile = new AssetFile("test.jpg"); File outputFile = new File(Environment.getExternalStorageDirectory(), assetFile.getName()); AssetsManager.copyAssetFile(getAssets(), assetFile.getAssetPath(), outputFile);
//复制Assets里的test文件夹到手机根目录 AssetsManager.copyAssetFile(getAssets(), "test", Environment.getExternalStorageDirectory());
|