本文主要是介绍Android TV开发--实现屏保图片云端可配置,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
功能描述:
在TV上,出厂时会默认配置一组屏保图片,用作屏保初期展示。
但作为运营的工具之一,当然会希望屏保图片可以在云端配置,若未配置则采用系统默 认图片。
若配置了一组新的图片,则需要下载到本地,在下次启动屏保时用来展示。
但作为运营的工具之一,当然会希望屏保图片可以在云端配置,若未配置则采用系统默 认图片。
若配置了一组新的图片,则需要下载到本地,在下次启动屏保时用来展示。
功能明确点:
1.屏保check时机:屏保启动
2.新屏保使用时机:屏保启动
3.屏保从云端下载图片后保存路径:
../files/screenPic/pathA/
../files/screenPic/pathB/
此处采用A/B目录来保存,便于在使用一组的同时下载另一组,类似于A/B系统
4.旧目录中图片删除时机:屏保退出时,发现刚进入时screenPath与退出时screenPath不一致(表示已同步成功,则删除之前使用目录中的图片)
5.支持设置屏保同步百分比阈值,大于该阈值则认为同步成功(比如10张成功8张以上),下次启动需要使用新屏保。 使用该目录屏保后,check云端时间与本地时间一致且发现上次未完全同步成功,则继续同步剩余未成功的图片。
2.新屏保使用时机:屏保启动
3.屏保从云端下载图片后保存路径:
../files/screenPic/pathA/
../files/screenPic/pathB/
此处采用A/B目录来保存,便于在使用一组的同时下载另一组,类似于A/B系统
4.旧目录中图片删除时机:屏保退出时,发现刚进入时screenPath与退出时screenPath不一致(表示已同步成功,则删除之前使用目录中的图片)
5.支持设置屏保同步百分比阈值,大于该阈值则认为同步成功(比如10张成功8张以上),下次启动需要使用新屏保。 使用该目录屏保后,check云端时间与本地时间一致且发现上次未完全同步成功,则继续同步剩余未成功的图片。
图片同步流程图:
流程提示:
1.在屏保进入时,check下载信息,根据云端、上次同步时间戳
2.在网络请求成功回调中开始图片同步
3.同步时需要判断图片是否已经在本地存在,若存在则copy,否则下载
4.整理出copy list和download list
5.下载包括MD5 check 机制和retry重试3次机制
6.当copy完毕且download完毕,统计成功率是否大于阈值
7.大于阈值值存屏保新路径,若达到100%则保存完整下载FullDLFlag
2.在网络请求成功回调中开始图片同步
3.同步时需要判断图片是否已经在本地存在,若存在则copy,否则下载
4.整理出copy list和download list
5.下载包括MD5 check 机制和retry重试3次机制
6.当copy完毕且download完毕,统计成功率是否大于阈值
7.大于阈值值存屏保新路径,若达到100%则保存完整下载FullDLFlag
补充:此处使用Json,来保存图片下载信息、图片copy,屏保按照云端配置的顺序显示,所以需要使用一种数据结构来存储相关信息,这里使用Json,通过更新Json信息达到,下载、使用图片的目的。
下面是代码说明部分
1.触发屏保图片同步入口
/*** picture whether need to update* this class call downloadhelper to download picture* @author WMB*/
public class PicCheckHelper {private static PicCheckHelper instance = null;private BaseTimer mTimer = null;private static final int REQUESTINTERVAL = 60 * 1000; // 2 * 60 * 60 * 1000public static PicCheckHelper getInstance(){if(null == instance){instance = new PicCheckHelper();}return instance;}/*** start 2h Timer*/public void start(){if(null == mTimer){mTimer = new BaseTimer();}mTimer.startInterval(REQUESTINTERVAL, mTimerCallback);}/*** close Timer*/public void close(){if(null != mTimer){mTimer.killTimer();}}/*** check right now*/public void checkNow(){checkPic();}/*** check picture whether need to update by cloud request*/private void checkPic() {GeneralHttpHelper.getInstance().requestScreenPicUpdate(mHttpCallback);} /*** 定时器回调接口*/private TimerCallBack mTimerCallback = new TimerCallBack() {@Overridepublic void callback() {checkPic();}};private HttpCallback mHttpCallback = new HttpCallback() {@Overridepublic void onState(HTTP_STATE state) {switch (state) {case STATE_SUCCESS:syncPic(); break;default:break;} }};/*** 执行图片同步入口*/private void syncPic() {PicSyncHelper.getInstance().syncPicture(); }
}
外部调用checkNow,立刻去云端检查是否有新的屏保,在网络请求成功回调中执行syncPic方法开始同步逻辑。
2.屏保同步解析类(解析云端请求信息)
/*** 屏保同步解析类*/
public class ScreenSyncParser extends BaseParser {@Overridepublic void run() {parserScreenData();}private void parserScreenData() {try {JSONObject jsonObj = new JSONObject(mParseData);if(jsonObj.optInt("status") < 0){sendMessage(HTTP_STATE.STATE_ERROR);LogHelper.releaseLog(PicCons.SCREEN_TAG, "ScreenSyncParser -- http request status < 0");return;}JSONObject data = jsonObj.optJSONObject("data");String timestamp = data.optString("updatetimes");LogHelper.releaseLog(PicCons.SCREEN_TAG, "ScreenSyncParser -- parserScreenData cloudtimestamp : "+ timestamp);Utils.setCloudTimestamp(timestamp);boolean needsync = compareTimeStamp(timestamp);// 比较云端时间戳与本地时间戳if(needsync){ // 云端时间新,则保存云端Json信息,为下载做准备(另一种情况:时间戳相等,上次下载80%,则保持之前存的Json不变,继续下载未成功的部分,后面会提到)JSONArray jsonData = data.optJSONArray("pics");if(null == jsonData || jsonData.length() == 0){return;}INFO_DL item = null;ArrayList<INFO_DL> resultList = new ArrayList<INFO_DL>();for (int i = 0; i < jsonData.length(); i++) {item = new INFO_DL();JSONObject jsonItem = jsonData.optJSONObject(i);item.dlIndex = i;item.md5 = jsonItem.optString("fileHash");item.url = jsonItem.optString("url");item.fileType = Utils.getPicTypeByUrl(item.url);resultList.add(item); }LogHelper.releaseLog(PicCons.SCREEN_TAG, "ScreenSyncParser -- parserScreenData needsync is true ,resultList size: "+resultList.size());saveDLJson(resultList); // 存到文件中,为下载做准备Utils.setFullDLFlag(true);// 若云端时间新,则默认上次同步已经完全成功,开始新的同步}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "ScreenSyncParser -- parserScreenData needsync is false don't need save DLJson");} sendMessage(HTTP_STATE.STATE_SUCCESS);} catch (Exception e) {sendMessage(HTTP_STATE.STATE_ERROR);LogHelper.releaseLog(PicCons.SCREEN_TAG, "ScreenSyncParser -- http request error!");}}/*** compare cloud and local timestamp* @param cloudTimestamp* @return true: cloud > local ,false:otherwise*/public boolean compareTimeStamp(String cloudTimestamp){String localTimestamp = Utils.getScreenTimestamp();if(TextUtils.isEmpty(localTimestamp)){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- compareTimeStamp localTimestamp is empty, need to sync");return true;}if(TextUtils.isEmpty(cloudTimestamp)){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- compareTimeStamp cloudTimestamp is empty, don't need to sync");return false;}try {long localTime = Long.parseLong(localTimestamp);long cloudTime = Long.parseLong(cloudTimestamp);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- compareTimeStamp cloudTimestamp is : "+cloudTimestamp+",localTimestamp is: "+localTimestamp);return cloudTime > localTime; } catch (Exception e) {e.printStackTrace();return false;}}/*** save download json* @param list*/public void saveDLJson(ArrayList<INFO_DL> list){if(null == list){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson ArrayList<INFO_DL> is null");return;}JSONArray jsonArray = new JSONArray();JSONObject jsonObj = null;INFO_DL infoItem = null;for (int i = 0; i < list.size(); i++) {jsonObj = new JSONObject();infoItem = new INFO_DL();infoItem = list.get(i);try {jsonObj.put("md5", infoItem.md5);jsonObj.put("url", infoItem.url);jsonObj.put("savePath", infoItem.savePath);jsonObj.put("DLFlag", infoItem.DLFlag);jsonObj.put("dlIndex", infoItem.dlIndex);jsonObj.put("reTry", infoItem.reTry);jsonObj.put("copyPath", infoItem.copyPath);jsonObj.put("fileType", infoItem.fileType);jsonArray.put(jsonObj);} catch (JSONException e) {e.printStackTrace();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson has an exception");return;} }LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson :"+jsonArray.toString());Utils.setDLJson(jsonArray.toString());}
}
同步帮助类
/*** this class is responsible for sync picture include download and copy,* @author WMB**/
public class PicSyncHelper {private static PicSyncHelper instance = null;private ArrayList<INFO_DL> DLJsonList = new ArrayList<INFO_DL>(); // 同步所用list(DLFlag为false)private ArrayList<INFO_DL> AllDLList = new ArrayList<INFO_DL>(); // 所有dlList不区分DLFlag(用于向硬盘中写下载数据)private ArrayList<INFO_DL> UsingJsonList = new ArrayList<INFO_DL>(); // 屏保图片展示所用listprivate String picCopyPath = ""; // 图片copy路径private boolean isDownloadEnd = false; // 下载完毕标志位private boolean isCopyEnd = false; // copy完毕标志位private String filePath = "";public static PicSyncHelper getInstance(){if(null == instance){instance = new PicSyncHelper();}return instance;}private PicSyncHelper(){filePath = Common.getContext().getFilesDir().getPath();}private Handler mHandle = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case PicCons.MESSAGE_DL:case PicCons.MESSAGE_CP:setScreenPath(true);break;case PicCons.MESSAGE_DL_PARTOK:isDownloadEnd = true;setScreenPath(false);break;case PicCons.MESSAGE_CP_PARTOK:isCopyEnd = true;setScreenPath(false);default:break;}}};/*** 执行图片同步逻辑*/public void syncPicture(){isDownloadEnd = false;isCopyEnd = false;if(DLJsonList == null || DLJsonList.size() <= 0){initDLJson(); // 初始化下载List}if(DLJsonList == null || DLJsonList.size() <= 0){ // 下载List为空,不做处理,退出LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- syncPicture , DLJsonList is empty ,do nothing ,exist!");return;}String usingJson = Utils.getUsingJson(); // 获取正在使用的Json信息Utils.setScreenTimestamp(Utils.getCloudTimestamp()); // 保存本地同步时间戳if(TextUtils.isEmpty(usingJson)){ // 全量下载LogHelper.releaseLog(PicCons.SCREEN_TAG, "--begin All download , download size is:"+DLJsonList.size());new Thread(new PicDownloadManager(DLJsonList, mHandle ,PicCons.MESSAGE_DL)).start();}else{ // 已存在则copy,不存在则下载try {JSONArray jsonArray = new JSONArray(usingJson);if(null != UsingJsonList && UsingJsonList.size() == 0){initUsingJsonList(jsonArray);}ArrayList<INFO_DL> dlList = new ArrayList<INFO_DL>();final ArrayList<INFO_DL> cpList = new ArrayList<INFO_DL>();INFO_DL Item = null;for (int i = 0; i < DLJsonList.size(); i++) {Item = DLJsonList.get(i);if(picisExist(Item.md5)){ //加入复制if(!TextUtils.isEmpty(picCopyPath)){Item.copyPath = picCopyPath;Item.savePath = getSavePath(picCopyPath);cpList.add(Item);}}else{ // 加入下载dlList.add(Item);}}if(dlList.size() > 0){// 下载if(cpList.size() > 0){LogHelper.releaseLog(PicCons.SCREEN_TAG, "--begin Part of download , download size is:"+dlList.size());new Thread(new PicDownloadManager(dlList,mHandle,PicCons.MESSAGE_DL_PARTOK)).start();}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "--begin All download ,using json exist, download size is:"+dlList.size());new Thread(new PicDownloadManager(dlList,mHandle,PicCons.MESSAGE_DL)).start();}}if(cpList.size() > 0){// 复制if(dlList.size() > 0){LogHelper.releaseLog(PicCons.SCREEN_TAG, "--begin Part copy, copy size is:"+cpList.size());new Thread(new Runnable() {@Overridepublic void run() {copy(cpList, PicCons.MESSAGE_CP_PARTOK);}}).start();}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "--begin All copy, copy size is:"+cpList.size());new Thread(new Runnable() {@Overridepublic void run() {copy(cpList, PicCons.MESSAGE_CP);}}).start(); }} } catch (JSONException e) {e.printStackTrace();}} } /*** update download json info * @param index* @param data*/public void updateDLJson(int index, INFO_DL data){if(null == AllDLList || AllDLList.size() == 0){String DLJsonStr = Utils.getDLJson();try {JSONArray DLJsonArr = new JSONArray(DLJsonStr);JSONObject updateItem = new JSONObject();updateItem.put("md5", data.md5);updateItem.put("url", data.url);updateItem.put("savePath", data.savePath);updateItem.put("DLFlag", data.DLFlag);updateItem.put("dlIndex", data.dlIndex);updateItem.put("reTry", data.reTry);updateItem.put("copyPath", data.copyPath);updateItem.put("fileType", data.fileType);DLJsonArr.put(index, updateItem);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- updateDLJson,DLJsonList is empty ,updata index : "+index+", jsonObj :"+updateItem.toString());Utils.setDLJson(DLJsonArr.toString());} catch (JSONException e) {LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- updateDLJson has an Exception");e.printStackTrace();return;}}else{if(index >= 0 && index < AllDLList.size()){AllDLList.remove(index);AllDLList.add(index, data);saveDLJson(AllDLList);}} }/*** save download json* @param list*/public void saveDLJson(ArrayList<INFO_DL> list){if(null == list){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson ArrayList<INFO_DL> is null");return;}JSONArray jsonArray = new JSONArray();JSONObject jsonObj = null;INFO_DL infoItem = null;for (int i = 0; i < list.size(); i++) {jsonObj = new JSONObject();infoItem = new INFO_DL();infoItem = list.get(i);try {jsonObj.put("md5", infoItem.md5);jsonObj.put("url", infoItem.url);jsonObj.put("savePath", infoItem.savePath);jsonObj.put("DLFlag", infoItem.DLFlag);jsonObj.put("dlIndex", infoItem.dlIndex);jsonObj.put("reTry", infoItem.reTry);jsonObj.put("copyPath", infoItem.copyPath);jsonObj.put("fileType", infoItem.fileType);jsonArray.put(jsonObj);} catch (JSONException e) {e.printStackTrace();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson has an exception");return;} }DLJsonList = list;LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- saveDLJson :"+jsonArray.toString());Utils.setDLJson(jsonArray.toString());}/*** 获取下载路径,如果上次未下载完全,则还将图片下载到之前使用的目录中* @return DLPath*/public String getDLPath(){boolean isFullDownload = Utils.getFullDLFlag();String usingPath = Utils.getScreenPath();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- now screenPath is:"+usingPath);String DLPath;if(TextUtils.isEmpty(usingPath)){DLPath = PicCons.SCREEN_PATH_A;}else{if(isFullDownload){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- isFUllDownload is true");DLPath = PicCons.SCREEN_PATH_A.equals(usingPath)? PicCons.SCREEN_PATH_B : PicCons.SCREEN_PATH_A;}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- isFUllDownload is false");DLPath = PicCons.SCREEN_PATH_A.equals(usingPath)? PicCons.SCREEN_PATH_A : PicCons.SCREEN_PATH_B;}}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- getDLPath : "+ filePath+DLPath);return filePath+DLPath;}/*** 判断图片是否在本地存在* @param md5* @return*/private boolean picisExist(String md5){if(null == UsingJsonList || UsingJsonList.size() <= 0){return false;}INFO_DL jsonItem = null;for (int i = 0; i < UsingJsonList.size(); i++) {jsonItem = UsingJsonList.get(i);if(md5.equals(jsonItem.md5)){picCopyPath = jsonItem.savePath;return true;}} return false;}/*** 初始化下载使用Json信息*/private void initDLJson(){String dlStr = Utils.getDLJson();if(!TextUtils.isEmpty(dlStr)){try {JSONArray jsonArray = new JSONArray(dlStr); if(null != jsonArray){ArrayList<INFO_DL> allDLList = Utils.transJsonToList(jsonArray);if(null != allDLList){int size = allDLList.size();INFO_DL item = null;if(size > 0){for (int i = 0; i < size; i++) {item = allDLList.get(i);AllDLList.add(item); if(!item.DLFlag){ // 将下载或copy失败的加进来DLJsonList.add(item);}}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- initDLJson success , DLJsonList size:"+DLJsonList.size());}}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- initDLJson allDLList is null, so DLJsonList is empty");}}} catch (Exception e) {LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- initDLJson has an Exception");e.printStackTrace();}}}/*** 初始化屏保图片使用Json信息* @param jsonArray*/private void initUsingJsonList(JSONArray jsonArray) {if(null == jsonArray){return;}UsingJsonList = Utils.transJsonToList(jsonArray); }/*** 根据图片拷贝路径,获得图片保存路径* @param copyPath 图片拷贝路径* @return 图片保存路径*/private String getSavePath(String copyPath){String usingPath = Utils.getScreenPath();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- getScreenPath --"+copyPath);String newPath = "";if(usingPath.contains(PicCons.SCREEN_PATH_A)){ // 从A到BnewPath = copyPath.replace(PicCons.SCREEN_PATH_A, PicCons.SCREEN_PATH_B);}else if(usingPath.contains(PicCons.SCREEN_PATH_B)){ // 从B到AnewPath = copyPath.replace(PicCons.SCREEN_PATH_B, PicCons.SCREEN_PATH_A);}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- getCopyFilePath --"+newPath);return newPath;}//复制图片,利用管道流提升效率private boolean copyAToB(String fileFrom , String fileTo){File from = new File(fileFrom);if(from.exists()){File to = new File(fileTo);if(!to.exists()){try {to.createNewFile();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copyAToB --create New file :" +to.getPath());} catch (IOException e) {e.printStackTrace();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copyAToB --create file has an Exception");return false;}}FileInputStream in = null;FileOutputStream out = null;FileChannel infc = null;FileChannel outfc = null;try {in = new FileInputStream (from);out = new FileOutputStream (to);infc = in.getChannel();outfc = out.getChannel();
// int temp = 0;
// while ((temp=in.read ())!=-1)
// {
// out.write (temp);
// }infc.transferTo(0, infc.size(), outfc);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copyAToB success from :"+ fileFrom+", to:"+fileTo);return true;} catch (Exception e) {e.printStackTrace();LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copyAToB has an Exception");return false;} finally {try {if(null != in){in.close();}if(null != out){out.close();}if(null != infc){infc.close();}if(null != outfc){outfc.close();}} catch (Exception e) {e.printStackTrace();}}}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copyAToB fileFrom:"+fileFrom+" is not exists");return false;}}private void copy(ArrayList<INFO_DL> cpList ,int messageType){if(null == cpList){return;}createFolder();INFO_DL Item = null;for (int i = 0; i < cpList.size(); i++) {Item = cpList.get(i);boolean copysuccess = copyAToB(Item.copyPath, Item.savePath);if(copysuccess){if(Utils.MD5Check(Item.savePath, Item.md5)){Item.DLFlag = true;LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- copy file MD5 check success , update Json Item");updateDLJson(Item.dlIndex, Item);}}}mHandle.sendEmptyMessage(messageType);}/*** 复制之前保证,PathA、PathB都存在*/private void createFolder(){File pathA = new File(filePath+PicCons.SCREEN_PATH_A);File pathB = new File(filePath+PicCons.SCREEN_PATH_B);if(!pathA.exists()){pathA.mkdirs();}if(!pathB.exists()){pathB.mkdirs();} }/*** 修改fullDLFlag的值,是否完全下载*/private void setFullDLFlag(boolean fullDLFlag) {Utils.setFullDLFlag(fullDLFlag);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- setFullDLFlag: "+fullDLFlag);}/*** 清空DLJsonList信息*/private void cleanDLJson(){if(null != DLJsonList){DLJsonList.clear();}}/*** 设置screenPath,图片成功数量大于约定上线,则设置,否则不设置* @param setNow 是否直接设置,用于限制下载和copy都完成,则执行该逻辑* @param isDownload */private void setScreenPath(boolean setNow){if(!setNow){if(!isDownloadEnd || !isCopyEnd){return;}} cleanDLJson();String DLJsonStr = Utils.getDLJson();int dlSuccessSize = 0;float dlTotalSize = 0;if(!TextUtils.isEmpty(DLJsonStr)){try {JSONArray DLJsonArr= new JSONArray(DLJsonStr);JSONObject DLJsonItem = null;dlTotalSize = DLJsonArr.length();for (int i = 0; i < dlTotalSize; i++) {DLJsonItem = DLJsonArr.optJSONObject(i);boolean dlSuccess = DLJsonItem.optBoolean("DLFlag");if(dlSuccess){dlSuccessSize++;}}if(dlTotalSize > 0){float DLSuccessRate = (float) dlSuccessSize / dlTotalSize; LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper --setScreenPath DLSuccessRate is:"+DLSuccessRate);if(DLSuccessRate >= PicCons.SHOW_LIMIT){String screenPath = getDLPath();if(screenPath.contains(PicCons.SCREEN_PATH_A)){Utils.setScreenPath(PicCons.SCREEN_PATH_A); // 设置显示目录为ALogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper --setScreenPath PATH_A");}else{Utils.setScreenPath(PicCons.SCREEN_PATH_B); // 设置显示目录为BLogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper --setScreenPath PATH_B");} Utils.setUsingJson(DLJsonStr); // 设置使用JsonLogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper -- setUsingJson :"+DLJsonStr);}if(DLSuccessRate == 1.0f){setFullDLFlag(true);}else{setFullDLFlag(false);}}} catch (Exception e) {}}}
}
其中主要方法为syncPicture,执行图片同步逻辑,启动线程下载、copy图片,完成后发送消息到Handler,执行setScreenPath方法。
Utils工具类
放置一些工具方法
public class Utils {public static String getPicTypeByUrl(String url){String picType = "";if(TextUtils.isEmpty(url)){return picType;}int lastPointIndex = url.lastIndexOf(".");if(-1 == lastPointIndex){return picType;}return url.substring(lastPointIndex, url.length());}public static boolean MD5Check(String filepath, String md5){if(TextUtils.isEmpty(filepath) || TextUtils.isEmpty(md5)){return false;}try {String fileMd5 = MD5Util.getFileMD5String(new File(filepath));return md5.equalsIgnoreCase(fileMd5);} catch (IOException e) {e.printStackTrace();}return false;}/*** JSONArray数组转为ArrayList* @param dlFlag* @param jsonArray* @return*/public static ArrayList<INFO_DL> transJsonToList(JSONArray jsonArray){if(null == jsonArray){return null;}ArrayList<INFO_DL> jsonList = new ArrayList<INFO_DL>();INFO_DL infoItem = null;JSONObject jsonObj = null;for (int i = 0; i < jsonArray.length(); i++) {infoItem = new INFO_DL();jsonObj = jsonArray.optJSONObject(i);infoItem.md5 = jsonObj.optString("md5");infoItem.url = jsonObj.optString("url");infoItem.DLFlag = jsonObj.optBoolean("DLFlag");infoItem.savePath = jsonObj.optString("savePath");infoItem.dlIndex = jsonObj.optInt("dlIndex");infoItem.reTry = jsonObj.optInt("reTry");infoItem.copyPath = jsonObj.optString("copyPath");infoItem.fileType = jsonObj.optString("fileType");jsonList.add(infoItem);}return jsonList;}/*** 根据固件版本获取序列号* @param version* @return*/public static String getServiceByVersion(String version){String service = "";if(TextUtils.isEmpty(version)){return service;}int index = version.indexOf("-");if(-1 == index){return service;}return version.substring(0, index);}/*** 存云端时间* * @param cloudTimestamp*/public static void setCloudTimestamp(String cloudTimestamp) {saveGlobalData(Define.KEY_SCREEN_CLOUDTIMESTAMP, cloudTimestamp);}/*** 获取云端时间戳* * @return 云端时间戳*/public static String getCloudTimestamp() {String cloudTimestamp = getGlobalData(Define.KEY_SCREEN_CLOUDTIMESTAMP);if (!TextUtils.isEmpty(cloudTimestamp)) {return cloudTimestamp;}return "";}/*** 存屏保同步的时间戳,用于和云端时间戳比较* * @param timestamp*/public static void setScreenTimestamp(String timestamp) {saveGlobalData(Define.KEY_SCREEN_TIMESTAMP, timestamp);}/*** 获取屏保同步时间戳* * @return 上次同步时间,第一次为""*/public static String getScreenTimestamp() {String timestamp = getGlobalData(Define.KEY_SCREEN_TIMESTAMP);if (!TextUtils.isEmpty(timestamp)) {return timestamp;}return "";}/*** 存当前屏保正在使用的图片目录* * @param screenUsingPath* 正在使用的图片目录*/public static void setScreenPath(String screenUsingPath) {saveGlobalData(Define.KEY_SCREEN_USINGPATH, screenUsingPath);}/*** 获取当前屏保正在使用的图片目录* * @return 屏保正在使用的目录,第一次为"",第一次进入屏保后该值应设置为PathA*/public static String getScreenPath() {String screenPath = getGlobalData(Define.KEY_SCREEN_USINGPATH);if (!TextUtils.isEmpty(screenPath)) {return screenPath;}return "";}/*** 存屏保当前使用Json* * @param usingJson*/public static void setUsingJson(String usingJson) {saveGlobalData(Define.KEY_SCREEN_JSONUSING, usingJson);}/*** 获取屏保当前使用Json* * @return 当前使用的Json文件信息,第一次为""*/public static String getUsingJson() {String usingJson = getGlobalData(Define.KEY_SCREEN_JSONUSING);if (!TextUtils.isEmpty(usingJson)) {return usingJson;}return "";}/*** 存下载图片使用Json* * @param DLJson*/public static void setDLJson(String DLJson) {saveGlobalData(Define.KEY_SCREEN_JSONDL, DLJson);}/*** 获取下载图片使用Json* * @return 下载图片用到的Json文件*/public static String getDLJson() {String DLJson = getGlobalData(Define.KEY_SCREEN_JSONDL);if (!TextUtils.isEmpty(DLJson)) {return DLJson;}return "";}/*** 存图片是否完全下载成功* * @param fullDL* true: 100%下载 ,false:未完全下载*/public static void setFullDLFlag(boolean fullDL) {String fullDLFlag = "0";if (fullDL) {fullDLFlag = "1";}saveGlobalData(Define.KEY_SCREEN_FULLDL, fullDLFlag);}/*** 获取图片是否完全下载成功* * @return true:完全下载,false:未完全下载*/public static boolean getFullDLFlag() {String fullDLFlag = getGlobalData(Define.KEY_SCREEN_FULLDL);if (!TextUtils.isEmpty(fullDLFlag) && "1".equals(fullDLFlag)) {return true;}return false;}private static void saveGlobalData(String key, String value){Common.getGlobalData().setGlobalData(key, value);}private static String getGlobalData(String key){return Common.getGlobalData().getGlobalData(key);}}
下载管理类
public class PicDownloadManager implements Runnable{private int dlIndex = 0;private INFO_DL dlInfo = null;private String dlPath = "";private Handler mHandler = null;private int messageType = 1; // 消息类型,用于区分是否全量下载(无copy)private ArrayList<INFO_DL> dlList = new ArrayList<INFO_DL>();/*** 下载管理类,负责从云端下载图片,包括下载成功后Md5Check,以及下载失败后最多3次重试* @param dlList 下载List* @param handler 下载完成后发送消息到主线程* @param messageType 发送消息类型(包括全量下载、部分下载)*/public PicDownloadManager(ArrayList<INFO_DL> dlList, Handler handler, int messageType){this.messageType = messageType;this.dlList = dlList;mHandler = handler;initDLPath();}/*** 初始化下载路径(绝对路径/data/data../PathA或PathB)*/private void initDLPath(){dlPath = PicSyncHelper.getInstance().getDLPath();}public void dlStart(){dlInfo = dlList.get(dlIndex);PicDownloader pic = new PicDownloader(dlPath ,dlInfo ,mDLListener);pic.download();}@Overridepublic void run() {dlStart();}/*** 下载监听,用于响应下载成功、下载失败以后的处理*/private IDLListener mDLListener = new IDLListener() {@Overridepublic void onFinish(INFO_DL info) {// md5 check// error retry,success dl nextif(Utils.MD5Check(info.savePath, info.md5)){ // check successLogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloadManager -- onFinish Md5check success");info.DLFlag = true;PicSyncHelper.getInstance().updateDLJson(info.dlIndex, info); // update dlJsonif(dlIndex < dlList.size() - 1){dlIndex++;dlInfo = dlList.get(dlIndex);PicDownloader pic = new PicDownloader(dlPath, dlInfo, mDLListener);pic.download();}else{mHandler.sendEmptyMessage(messageType);}}else{ // download errordealError(info);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloadManager -- onFinish Md5check error");}}@Overridepublic void onError(INFO_DL info) {// retry limit , download nextdealError(info);LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloadManager -- onError");}};/*** 处理下载失败的情况(包括下载成功但MD5 check失败的情况)* @param info*/private void dealError(INFO_DL info){if(info.reTry < PicCons.MAX_RETRY){info.reTry ++;PicDownloader pic = new PicDownloader(dlPath, info, mDLListener);pic.download();}else{if(dlIndex < dlList.size() - 1){dlIndex++;dlInfo = dlList.get(dlIndex);PicDownloader pic = new PicDownloader(dlPath, dlInfo, mDLListener);pic.download();}else{mHandler.sendEmptyMessage(messageType);}}}public interface IDLListener {void onError(INFO_DL info);void onFinish(INFO_DL info);}}}
下载类
public class PicDownloader{private String savePath;private INFO_DL DLInfo;private IDLListener listener;private String picName;/*** 执行图片下载类* @param savePath 图片保存的路径* @param DLInfo 下载封装信息体* @param listener 下载状态回调*/public PicDownloader(String savePath,INFO_DL DLInfo,IDLListener listener){this.savePath = savePath;this.DLInfo = DLInfo;this.listener = listener;picName = DLInfo.md5+DLInfo.fileType;}/*** 开始下载*/public void download() {HttpURLConnection conn = null;try {conn = (HttpURLConnection) new URL(DLInfo.url).openConnection();conn.setConnectTimeout(PicCons.DEFAULT_TIMEOUT);conn.setReadTimeout(PicCons.DEFAULT_TIMEOUT);addRequestHeaders(conn);final int code = conn.getResponseCode();LogHelper.releaseLog(PicCons.SCREEN_TAG,"PicDownloader -- run getResponseCode : " + code);if (PicCons.HTTP_OK == code) {dlInit(conn, code);}else{LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloader -- run listener.onError");if(null != listener){listener.onError(DLInfo);}return;}} catch (Exception e) {e.printStackTrace();if(null != listener){listener.onError(DLInfo);}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloader -- run has an Exception");} finally {if (null != conn) conn.disconnect();}}private void dlInit(HttpURLConnection conn, int code){if (!createFile(savePath, picName)){LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloader -- dlInit Can not create file:"+savePath+"picName");if(null != listener){listener.onError(DLInfo);}return;}dlData(conn);}private void dlData(HttpURLConnection conn){InputStream is = null;FileOutputStream fos = null;try {is = conn.getInputStream();fos = new FileOutputStream(new File(savePath, picName));byte [] b = new byte[4096];int len;while((len = is.read(b)) != -1){fos.write(b, 0, len);}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloader -- dlData download finish : "+savePath+"-"+DLInfo.md5);if(null != listener){listener.onFinish(DLInfo);}} catch (IOException e) {e.printStackTrace();if(null != listener){listener.onError(DLInfo);}LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloader -- dlData has an Exception");return;} finally {try {if(null != is){is.close();}if(null != fos){fos.close();}} catch (IOException e) {e.printStackTrace();}}}private synchronized boolean createFile(String path, String fileName) {boolean hasFile = false;try {File dir = new File(path);boolean hasDir = dir.exists() || dir.mkdirs();if (hasDir) {File file = new File(dir, fileName);hasFile = file.exists() || file.createNewFile();DLInfo.savePath = file.getPath();}} catch (IOException e) {e.printStackTrace();}return hasFile;}private void addRequestHeaders(HttpURLConnection conn) {conn.addRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg," +"application/x-shockwave-flash, application/xaml+xml," +"application/vnd.ms-xpsdocument, application/x-ms-xbap," +"application/x-ms-application, application/vnd.ms-excel," +"application/vnd.ms-powerpoint, application/msword, */*");conn.addRequestProperty("Charset", "UTF-8");conn.addRequestProperty("Connection", "Keep-Alive");conn.addRequestProperty("Accept-Encoding", "identity");}}
谢谢。
这篇关于Android TV开发--实现屏保图片云端可配置的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!