package tv.avfun.util; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.Writer; import java.util.Calendar; import java.util.List; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import tv.ac.fun.AcApp; import tv.ac.fun.BuildConfig; import tv.avfun.api.Bangumi; import tv.avfun.api.BangumiList; import tv.avfun.api.Channel; import tv.avfun.api.ChannelList; import android.text.TextUtils; import android.util.Log; /** * 数据获取与缓存。单例 * * @author Yrom * */ public class DataStore { private File timeListCachedFile_0,timeListCachedFile_1, channelListCachedFile,homeCachedFile; private static final String TAG = DataStore.class.getSimpleName(); private static final DataStore instance = new DataStore(); private ChannelList channelList = new ChannelList(); private BangumiList bangumiList = new BangumiList(); /** 30 min */ public static final long CHANNEL_EXPIRED = 30 * 60000; /** 1 hour */ public static final long CHANNEL_LIST_EXPIRED = 2 * CHANNEL_EXPIRED; /** 1 days */ public static final long TIME_LIST_EXPIRED = 24 * CHANNEL_LIST_EXPIRED; /** 首页频道列表缓存文件 */ public static final String CHANNEL_LIST_CACHE = "channel_list.dat"; /** 番组列表缓存文件 */ public static final String TIME_LIST_CACHE_0 = "time_date_0.dat"; public static final String TIME_LIST_CACHE_1 = "time_date_1.dat"; public static final String HOME_CACHE = "home.dat"; private DataStore() { initCache(); } private void initCache() { timeListCachedFile_0 = new File(AcApp.context().getCacheDir(), TIME_LIST_CACHE_0); timeListCachedFile_1 = new File(AcApp.context().getCacheDir(), TIME_LIST_CACHE_1); channelListCachedFile = new File(AcApp.context().getCacheDir(), CHANNEL_LIST_CACHE); homeCachedFile = new File(AcApp.context().getCacheDir(), HOME_CACHE); } public static DataStore getInstance() { return instance; } // ======================================================= // 番组列表 // ======================================================= /** * @return 没有缓存文件,或当前为星期天且缓存不是今天缓存的<br> * 或者缓存时间超过 {@link #TIME_LIST_EXPIRED},返回false */ public boolean isBangumiListCached(int ulIndex) { if (!readTimeListCache(ulIndex)) return false; // no cache Calendar calendar = Calendar.getInstance(); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); long cache = calendar.getTimeInMillis() - bangumiList.cacheTime; if( cache >= TIME_LIST_EXPIRED ) // 缓存超过TIME_LIST_EXPIRED return false; else if(dayOfWeek == 1){ int currentDay = calendar.get(Calendar.DAY_OF_MONTH); calendar.setTimeInMillis(bangumiList.cacheTime); int cacheDay = calendar.get(Calendar.DAY_OF_MONTH); return currentDay >= cacheDay; } else return true; } /** 加载缓存的番组列表 */ public List<Bangumi[]> loadTimeList(int ulIndex) { synchronized (this.bangumiList) { if(!readTimeListCache(ulIndex)) return null; return this.bangumiList.bangumiTimeList; } } private boolean readTimeListCache(int ulIndex) { Object obj = null; if(ulIndex >0) obj = readObject(timeListCachedFile_1.getAbsolutePath()); else obj = readObject(timeListCachedFile_0.getAbsolutePath()); if (obj != null && obj instanceof BangumiList) { this.bangumiList.bangumiTimeList = ((BangumiList) obj).bangumiTimeList; this.bangumiList.cacheTime = ((BangumiList) obj).cacheTime; return true; } return false; } /** * 保存番组列表到缓存文件 * * @param list * - 当天的番组列表 * @return list为空或保存失败则返回false */ public boolean saveTimeList(List<Bangumi[]> list, int ulIndxe) { if (list != null && list.size()>0) { this.bangumiList.bangumiTimeList = list; this.bangumiList.cacheTime = System.currentTimeMillis(); if(ulIndxe>0) return writeObject(timeListCachedFile_1.getAbsolutePath(), this.bangumiList); else return writeObject(timeListCachedFile_0.getAbsolutePath(), this.bangumiList); } return false; } public Document getHomeCache(){ if(homeCachedFile.lastModified() + TIME_LIST_EXPIRED > System.currentTimeMillis()){ String str = readFromFile(homeCachedFile.getAbsolutePath()); if(!TextUtils.isEmpty(str)){ return Jsoup.parse(str); } } return null; } public boolean saveHomeCache(Document doc){ return writeToFile(homeCachedFile.getAbsolutePath(), doc.toString()); } // ======================================================= // 首页频道列表 // ======================================================= /** * <b>废弃</b>。调用者应由{@link #getChannelListLastUpdateTime()}来判断是否有缓存 * @return 没有缓存文件,或缓存时间超过 {@link #CHANNEL_LIST_EXPIRED}<br> * 或者主页显示模式被更改了 * 则返回false */ @Deprecated public boolean isChannelListCached() { synchronized (this.channelList) { if (this.channelList.channels == null) { if (!readChannelListCache()) return false; } if(!AcApp.getHomeDisplayMode().equals(this.channelList.displayMode)) return false; return this.channelList.cacheTime + CHANNEL_LIST_EXPIRED >= System.currentTimeMillis(); } } public boolean isDisplayModeChanged(){ return !AcApp.getHomeDisplayMode().equals(this.channelList.displayMode); } /** 加载频道列表缓存 */ public Channel[] loadChannelList() { synchronized (this.channelList) { if (this.channelList.channels == null) { if(!readChannelListCache()) return null; } return this.channelList.channels; } } private boolean readChannelListCache() { Object obj = readObject(channelListCachedFile.getAbsolutePath()); if (obj != null && obj instanceof ChannelList) { this.channelList.channels = ((ChannelList) obj).channels; this.channelList.cacheTime = ((ChannelList) obj).cacheTime; return true; } return false; } /** * 获取上次更新时间 * @return 没有缓存则返回 -1 */ public long getChannelListLastUpdateTime(){ synchronized (this.channelList) { if (this.channelList.channels == null) { if(!readChannelListCache()) return -1; } return this.channelList.cacheTime; } } /** * 保存频道列表到缓存文件 * * @param list * - 当天的频道列表 * @return list为空或保存失败则返回false */ public boolean saveChannelList(Channel[] list) { if (list != null) { this.channelList.displayMode = AcApp.getHomeDisplayMode(); this.channelList.channels = list; this.channelList.cacheTime = System.currentTimeMillis(); return writeObject(channelListCachedFile.getAbsolutePath(), this.channelList); } return false; } // ======================================================= // 静态方法 // ======================================================= /** * 读取缓存的对象 * * @param path * - 缓存的对象文件路径 * @return 返回null,如果读取失败。 */ public static Object readObject(String path) { if (TextUtils.isEmpty(path.trim())) throw new IllegalArgumentException("path 不能为空!"); try { ObjectInputStream in = new ObjectInputStream(new FileInputStream(path)); Object obj = in.readObject(); in.close(); return obj; } catch (Exception e) { if (BuildConfig.DEBUG) Log.e(TAG, "failed to read object from " + path +"\n"+ e.getMessage()); return null; } } /** * 写到缓存文件 * * @param path * - 文件路径 * @param obj * - 缓存的对象 * @return false,如果写失败 */ public static boolean writeObject(String path, Serializable obj) { if(obj == null) throw new IllegalArgumentException("obj can not be null"); try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path)); out.writeObject(obj); out.close(); return true; } catch (Exception e) { if (BuildConfig.DEBUG) Log.e(TAG, "can not write obj to " + path, e); } return false; } /** * 读取字符串 * * @return 文件不存在或读取失败,返回null */ public static String readFromFile(String path) { try { File file = new File(path); if (file.exists()) { String str = new String(readDataFromFile(file), "utf-8"); return str; } } catch (Exception e) { if (BuildConfig.DEBUG) Log.e(TAG, "can not read from file " + path+"\n"+ e.getMessage()); } return null; } /** * 写字符串到文件 * * @param path * - 文件路径 * @param str * - 字符串,not null * @return 写成功返回true */ public static boolean writeToFile(String path, String str) { if (TextUtils.isEmpty(path) || TextUtils.isEmpty(str.trim())) throw new IllegalArgumentException("path 或 str 不能为null、空白或空字符串!"); File file = new File(path); try { if(!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } if(file.exists()) file.delete(); // 确保是一个 file.createNewFile(); // 全新的文件 Writer writer = new FileWriter(file); writer.write(str); writer.close(); return true; } catch (IOException e) { if (BuildConfig.DEBUG) Log.e(TAG, "can not write to file " + path, e); } return false; } /** 读取字节数组 */ public static byte[] readDataFromFile(File file) throws IOException { return readData(new FileInputStream(file)); } /** 读取字节数组 */ public static byte[] readData(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int len = 0; while ((len = in.read(buffer)) != -1) baos.write(buffer, 0, len); in.close(); return baos.toByteArray(); } public static String readData(InputStream in, String charset) throws IOException{ return new String(readData(in),charset); } public static String getChannelCacheFile(int channelId){ return AcApp.context().getCacheDir()+"/"+channelId+".dat"; } public static boolean saveChannel(Channel channel) { return writeObject(getChannelCacheFile(channel.channelId), channel); } public static boolean isChannelCached(int channelId){ long lastModified = new File(getChannelCacheFile(channelId)).lastModified(); return System.currentTimeMillis() - lastModified < CHANNEL_EXPIRED; } public static Channel getCachedChannel(int channelId){ Object obj = readObject(getChannelCacheFile(channelId)); if(obj!=null && obj instanceof Channel) return (Channel)obj; else return null; } // public static String getJsonCachePath(int channelId){ // return AcApp.context().getExternalCacheDir()+"/"+channelId+".json"; // } // /** 保存原始json数据 */ // public static boolean saveJson(int channelId, String json){ // // // return writeToFile(getJsonCachePath(channelId), json); // } // /** 读取缓存的json数据 */ // public static JSONObject readJson(int channelId){ // // try { // return new JSONObject(readFromFile(getJsonCachePath(channelId))); // } catch (JSONException e) { // Log.e(TAG, "failed to read json of channel"+channelId ,e); // return null; // } // } // /** Returns 0 if the json file does not exist */ // public static long getJsonLastUpdateTime(int channelId){ // // File jsonCacheFile = new File(getJsonCachePath(channelId)); // return jsonCacheFile.lastModified(); // // } }