Android 架构:使用枚举重新设计缓存目录的构建

首先声明,这篇文章不是讲getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()等等文件目录的存放位置,及使用方法的。

本文章,主要是想说下通过枚举来管理缓存目录(就是上面那么目录),是否比我们常用的常量管理缓存目录更有优势。

我们先看下Android下的缓存目录
在这里插入图片描述
上面就是一般,我们需要用的一些缓存数据的目录,比如配置文件,广告,数据缓存等等。



通常情况下,在管理缓存数据的目录结构的时候,我是怎么操作的呢。

  • 1,首先创建一个路径管理类
  • 2,然后,创建缓存目录的常量。比如,adsPath=rootPath+"/ads/";
  • 3,使用时候,判断初始化,把路径引用过去。save(adsPath+xx.png)

这样做有什么不好的地方呢?

  • 1,初始化缓存目录不方面,每次用的时候,都需要判断是否创建。
  • 2,到处引用路径,如果管理不好,想要修改的话,会是个灾难。
  • 3,缓存文件,有被修改的可能。如果,有人没有把路径写到这个类里面呢?

下面,我们尝试使用枚举来管理缓存的路径,看看是否更优雅,更有优势呢。

当然,Android不建议使用枚举,因为枚举的每个变量其实,都会生成一个对象属性,并且还会有一个包换所有属性的对象数组。这样,相比与其他常量来说会带来更大的内存消耗。但是,使用枚举来管理,也有它的优势。(不然,还写啥呢…)

使用枚举有很多优势:

  • 安全
  • 构造方法私有
  • 管理,操作方便

比如,多账号登陆,每个账号下的缓存文件夹的功能差不多,图片,用户信息等。
在这里插入图片描述

这样的话,通过常量的方式来管理缓存目录,就需要不断的拼接字符串,来实现每个账号下的缓存目录,既不方便,也容易出错。

下面,就通过枚举来设计一个缓存文件夹管理的架构。看看是不是比我们常用的方式,更方便呢?

创建步骤:

1,创建一个通用的缓存文件目录

一般来说,图片缓存,数据缓存都是我们常用的,我们就添加这两个缓存目录

/**
 * cache中缓存的文件路径
 */
public enum CommonCacheFile {
  /**
   * cache的图片文件路径。
   */
  CACHE_IMAGE("/common/image/"),
  /**
   * 文件的缓存路径
   */
  CACHE_FILE("/common/file/");


  private String path;

  CommonCacheFile(String path) {
    this.path = path;
  }

  public String getValue() {
	//缓存的根目录+缓存的相对路径
    return SDCardUtils.getAppCacheFile(false) + path;
  }
}

这个枚举,保存的都是我们缓存的相对路径。通过getValue()方法,来获取绝对路径。这样,控制我们的缓存路径是不是看着更加直观呢。

下面,我们为多账号登陆也设计一个枚举类。

2,创建每个账号下的缓存路径

有时候,我们需要设计多账号的缓存目录,每个账号下的缓存目录结构,基本是一样的。
这个时候,如果使用常量,是不是就没有那么方便了。

我们创建一个枚举类来实现。


public enum UserFile {
  /**
   * 用户信息路径
   */
  fileUserInfo("/userInfo/");

  private String value;

  UserFile(String path) {
    this.value = path;
  }

  public String getValue() {
	//这里,我们模拟一个userId(12345)。
    return SDCardUtils.getAppSubFile(false, "12345") + value;
  }
}

通过上面的枚举,我们只要动态替换了"12345",就达到了多账号,缓存目录的创建。
相当于根目录+userId+缓存目录相对路径

缓存目录的枚举创建好了之后,我们就需要在进入APP的时候,把我们的文件夹都初始化。
这样,也就避免了我们每次使用的时候需要判断的缺点。

3,创建初始化缓存目录类

我们刚进入APP的时候,就把我们需要的缓存文件目录都初始化。
这样的好处:

  • 1,不用每次使用的时候,都需要判断是否初始化。
  • 2,如果忘了初始化,就会造成IO异常。
public class FileEngine {

  public static void init(Context applicationContext) {
    SDCardUtils.init(applicationContext);
    //通用的缓存目录 枚举 。通过values()获取所有的缓存目录
    CommonCacheFile[] cacheFiles = CommonCacheFile.values();
	//用户下的缓存目录 枚举
    UserFile[] userFiles = UserFile.values();

    List<String> path = new ArrayList<>();
    //把所有路径,都放到集合
	for (CommonCacheFile cacheFile : cacheFiles) {
		
      path.add(cacheFile.getValue());
    }
    for (CommonFile cacheFile : files) {
      path.add(cacheFile.getValue());
    }
      path.add(cacheFile.getValue());
    }
	//创建所有的缓存文件目录
    for (String p : path) {
      initDir(new File(p));
    }
  }

  private static boolean initDir(File dir) {
    return FileUtils.createDir(dir);
  }
}

这里,我们通过枚举的values()方法,拿到所有的缓存目录的集合,然后,全部初始化。以后,我们枚举如果添加新的缓存目录,这里也不需要改动。

4,初始化缓存目录及效果

这里,我们就找个Activity初始化,看下效果。


public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
	//这样,我们的缓存目录就初始化好了
    FileEngine.init(getApplicationContext());
  }
}

这里,我们通过一行代码,所有需要的缓存目录就都初始化好了,是不是很简单。

到这里,我们的APP的缓存目录管理,就做完了。

通过枚举来管理缓存目录,确实比常量要消耗更多的内存。但是,这样我们可以更加直观的管理缓存,操作上也方便很多,尤其在多账号的时候,作用更明显。

下面看下效果

在这里插入图片描述
这里,我们就看到了,我们调用初始化方法后。我们的common缓存跟账户12345下面的userInfo缓存目录就已经创建好了。

如果,我们想实现多账号的缓存目录管理的话,我们只需要动态修改UserFile枚举里的 12345就可以达到目的了。

public enum UserFile {
  /**
   * 用户信息路径
   */
  fileUserInfo("/userInfo/");

  private String value;

  UserFile(String path) {
    this.value = path;
  }

  public String getValue() {
    return SDCardUtils.getAppSubFile(false, "12345") + value;
  }
}

只需要动态替换掉 12345这个账号,就会在对应账号下创建缓存目录了。

这样的多账号缓存设计,是不是更优雅呢。

通过枚举实现缓存目录的管理,到这里就完成了。

5,其他的工具类

FileUtil,文件工具类

public class FileUtils {

  public static boolean createDir(File dir) {
    if (dir == null) {
      return false;
    }
    if (checkFileExist(dir)) {
      return true;
    }
    return makeDir(dir);
  }

  private static boolean checkFileExist(File file) {
    return file != null && file.exists();
  }

  private static boolean makeDir(File dir) {
    if (dir.exists() && !dir.isFile()) {
      return true;
    }
    return dir.mkdirs();
  }

}

权限工具类

public class PermissionUtils {

    private static volatile PermissionUtils sInstance;

    public static final String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};

    private PermissionUtils() {
    }

    public static PermissionUtils getsInstance() {
        if (sInstance == null) {
            sInstance = new PermissionUtils();
        }
        return sInstance;
    }

    public boolean checkPermission(Context context, String... permissions) {

        for (String permission : permissions) {
            // 判断当前该权限是否允许
            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断是否需要动态申请权限
     */
    public static boolean needRequestPermission() {
        return Build.VERSION.SDK_INT >= 23;
    }
}

SD卡工具类,获取根目录


public class SDCardUtils {

  private static Context context;

  public static void init(Context application) {
    context = application;
  }


  /**
   * 获取app 的file
   */
  public static String getAppFile(boolean internal) {
    if (internal) {
      return getAppInternalDir();
    }
    return getAppExternalDir();
  }

  /**
   * 获取app 的cache File
   */
  public static String getAppCacheFile(boolean internal) {
    if (internal) {
      return getAppInternalCacheDir();
    }
    return getAppExternalCacheDir();
  }

  /**
   * 这里是提供 根目录下的二级目录
   */
  public static String getAppSubFile(boolean internal, String name) {
    return getAppFile(internal) + File.separator + "liu" + File.separator + name + File.separator;
  }

  public static String getAppInternalDir() {
    return context.getFilesDir().getPath();
  }

  public static String getAppInternalCacheDir() {
    return context.getCacheDir().getPath();
  }

  public static String getAppExternalDir() {
    String path = "";
    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
        && hasSDCardPermission()) {
      File fileDir = context.getExternalFilesDir(null);
      if (fileDir != null) {
        path = fileDir.getPath();
      } else {
        fileDir = context.getFilesDir();
        if (fileDir != null) {
          path = fileDir.getPath();
        }
      }
    }
    return path;
  }

  public static String getAppExternalCacheDir() {
    String path = "";
    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
        && hasSDCardPermission()) {
      File fileDir = context.getExternalCacheDir();
      if (fileDir != null) {
        path = fileDir.getPath() ;
      } else {
        fileDir = context.getFilesDir();
        if (fileDir != null) {
          path = fileDir.getPath();
        }
      }
    }
    return path;
  }


  public static boolean hasSDCardPermission() {
    return (!PermissionUtils.needRequestPermission()
        || PermissionUtils.getsInstance()
        .checkPermission(context, PermissionUtils.PERMISSIONS_STORAGE));
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章