安卓开发中日期格式化工具类封装建议
日期格式化是我们日常开发中很常见的操作。但是系统给我们建议用的api看的我是眼花缭乱,什么getDateInstance(),getDateTimeInstance()…看完之后一头雾水,完全不知道它们到底有多大差别。所以我打算重新封装一下这个操作。
众所周知,我们可爱的小伙伴SimpleDateFormat是线程不安全的。所以我们需要借助TreadLocal来保证每个线程的SimpleDateFormat的唯一性。如果你不知道什么是TeadLocal,建议你先去了解一下。
第一步我们先搞个单例模式,并声明一个日期格式化类的缓存类。当然这里的LruCache换成Map也可以,但是在安卓开发中内存是很宝贵的,LruCache对内存会更加友好一点。
public class DateFormatHelper {
/**
* 默认缓存的日期格式器长度,可根据实际使用情况修改此值
*/
private static final int DEFAULT_FORMATTER_CACHE_LENGTH = 5;
/**
* 用于保存我们的日期格式器
*/
private final LruCache<String, ThreadLocal<SimpleDateFormat>> dateFormatterCache;
private DateFormatHelper() {
dateFormatterCache = new LruCache<>(DEFAULT_FORMATTER_CACHE_LENGTH);
}
private static final class InstanceHolder {
private static final DateFormatHelper INSTANCE = new DateFormatHelper();
}
public static DateFormatHelper getInstance() {
return InstanceHolder.INSTANCE;
}
}
然后把咋们最喜欢用的format方法和parse方法也在我们这个DateFormatHelper搞一个。
public class DateFormatHelper {
............
/**
* 日期格式化方法
*
* @param date 待格式化的日期
* @param pattern 日期的指定格式
* @return 格式化之后的日期
*/
public String format(Date date, String pattern) {
ThreadLocal<SimpleDateFormat> dateFormatterThreadLocal = dateFormatterCache.get(pattern);
if (null == dateFormatterThreadLocal) {
dateFormatterThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
dateFormatterCache.put(pattern, dateFormatterThreadLocal);
}
SimpleDateFormat targetDateFormatter = dateFormatterThreadLocal.get();
return targetDateFormatter.format(date);
}
/**
* 解析字符串为Date对象
*
* @param source 待解析的字符串
* @param pattern 字符串的格式
* @return 解析好的日期
* @throws ParseException 解析时可能抛出的异常
*/
public Date parse(String source, String pattern) throws ParseException {
ThreadLocal<SimpleDateFormat> dateFormatterThreadLocal = dateFormatterCache.get(pattern);
if (null == dateFormatterThreadLocal) {
dateFormatterThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
dateFormatterCache.put(pattern, dateFormatterThreadLocal);
}
SimpleDateFormat targetDateFormatter = dateFormatterThreadLocal.get();
return targetDateFormatter.parse(source);
}
}
很明显,调用SimpleDateFormat的format和parse之前的代码都是重复的。所以我们这里抽离一个方法获取SimpleDateFormat。
public class DateFormatHelper {
............
/**
* 日期格式化方法
*
* @param date 待格式化的日期
* @param pattern 日期的指定格式
* @return 格式化之后的日期
*/
public String format(Date date, String pattern) {
SimpleDateFormat targetDateFormatter = getDateFormatterByPattern(pattern);
return targetDateFormatter.format(date);
}
/**
* 解析字符串为Date对象
*
* @param source 待解析的字符串
* @param pattern 字符串的格式
* @return 解析好的日期
* @throws ParseException 解析时可能抛出的异常
*/
public Date parse(String source, String pattern) throws ParseException {
SimpleDateFormat targetDateFormatter = getDateFormatterByPattern(pattern);
return targetDateFormatter.parse(source);
}
/**
* 根据日期格式生成SimpleDateFormat
* @param pattern 日期格式
* @return 生成的SimpleDateFormat
*/
private SimpleDateFormat getDateFormatterByPattern(String pattern) {
ThreadLocal<SimpleDateFormat> dateFormatterThreadLocal = dateFormatterCache.get(pattern);
if (null == dateFormatterThreadLocal) {
dateFormatterThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
dateFormatterCache.put(pattern, dateFormatterThreadLocal);
}
return dateFormatterThreadLocal.get();
}
}
这里的getDateFormatterByPattern还有点问题就是在多线程并发调用的情况下可能导致一个pattern put了多个dateFormatterThreadLocal,所以我们这里加个锁,来个双重非空判断。
public class DateFormatHelper {
private static final Object lockObj = new Object();
............
private SimpleDateFormat getDateFormatterByPattern(String pattern) {
ThreadLocal<SimpleDateFormat> dateFormatterThreadLocal = dateFormatterCache.get(pattern);
if (null == dateFormatterThreadLocal) {
synchronized (lockObj) {
dateFormatterThreadLocal = dateFormatterCache.get(pattern);
if (null == dateFormatterThreadLocal) {
dateFormatterThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(pattern));
dateFormatterCache.put(pattern, dateFormatterThreadLocal);
}
}
}
return dateFormatterThreadLocal.get();
}
}
嗯,还有点不完美就是没有对参数进行非空判断。
public class DateFormatHelper {
............
public String format(Date date, String pattern) {
checkNotNull(date, "date不能为空");
checkNotNull(pattern, "pattern均不能为空");
..........
}
public Date parse(String source, String pattern) throws ParseException {
checkNotNull(source, "source不能为空");
checkNotNull(pattern, "pattern不能为空");
........
}
/**
* 检验的工具方法,若target为null,则抛异常
* @param target 检测的目标
* @param errorMessage target为null的时候抛出的异常提示
*/
private static void checkNotNull(Object target, String errorMessage) {
if (null == target) {
throw new IllegalArgumentException(errorMessage);
}
}
}
嗯,基本已经完成了。然后我们在实际使用的时候定义一个pattern的常量类,我们实际使用的时候就这样调用。
String formatDate = DateFormatHelper.getInstance()
.format(new Date(), DateFormatPatternConstants.DEFAULT_PATTERN);