Android TV开发--实现屏保图片云端可配置

功能描述:

 在TV上,出厂时会默认配置一组屏保图片,用作屏保初期展示。
 但作为运营的工具之一,当然会希望屏保图片可以在云端配置,若未配置则采用系统默  认图片。
 若配置了一组新的图片,则需要下载到本地,在下次启动屏保时用来展示。


功能明确点:

1.屏保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


 补充:此处使用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 * 1000
	
	public 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() {
		
		@Override
		public void callback() {
			checkPic();
		}
	};
	
	private HttpCallback mHttpCallback = new HttpCallback() {
		
		@Override
		public 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 {

	@Override
	public 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>(); // 屏保图片展示所用list
	private 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(){

		@Override
		public 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() {
							
							@Override
							public 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() {
							
							@Override
							public 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到B
			newPath = copyPath.replace(PicCons.SCREEN_PATH_A, PicCons.SCREEN_PATH_B);
		}
		else if(usingPath.contains(PicCons.SCREEN_PATH_B)){ // 从B到A
			newPath = 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); // 设置显示目录为A
							LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper --setScreenPath PATH_A");
						}
						else{
							Utils.setScreenPath(PicCons.SCREEN_PATH_B); // 设置显示目录为B
							LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicSyncHelper --setScreenPath PATH_B");
						}						
						Utils.setUsingJson(DLJsonStr);  // 设置使用Json
						LogHelper.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();
	}
	
	@Override
	public void run() {
		dlStart();
	}
	
	/**
	 * 下载监听,用于响应下载成功、下载失败以后的处理
	 */
	private IDLListener mDLListener = new IDLListener() {
		
		@Override
		public void onFinish(INFO_DL info) {
			// md5 check
			// error retry,success dl next
			if(Utils.MD5Check(info.savePath, info.md5)){ // check success
				LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloadManager -- onFinish Md5check success");
				info.DLFlag = true;
				PicSyncHelper.getInstance().updateDLJson(info.dlIndex, info); // update dlJson
				if(dlIndex < dlList.size() - 1){
					dlIndex++;
					dlInfo = dlList.get(dlIndex);
					PicDownloader pic = new  PicDownloader(dlPath, dlInfo, mDLListener);
					pic.download();
				}
				else{
					mHandler.sendEmptyMessage(messageType);
				}
			}
			else{ // download error
				dealError(info);
				LogHelper.releaseLog(PicCons.SCREEN_TAG, "PicDownloadManager -- onFinish Md5check error");
			}
		}
		
		@Override
		public void onError(INFO_DL info) {
			// retry limit , download next
			dealError(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");
            
    }

}

上面就是图片同步的整个过程,欢迎大家讨论。

谢谢。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值