DownloadManager:

package com.study.googleplay.manager;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ConcurrentHashMap;import android.content.Intent;import android.net.Uri;import com.study.googleplay.db.dao.DownloadInfoDao;import com.study.googleplay.domain.AppInfo;import com.study.googleplay.domain.DownloadInfo;import com.study.googleplay.http.HttpHelper;import com.study.googleplay.http.HttpHelper.HttpResult;import com.study.googleplay.utils.IOUtils;import com.study.googleplay.utils.LogUtils;import com.study.googleplay.utils.UIUtils;/** * 下载管理器 *  *  * -未下载-等待下载-正在下载-暂停下载-下载失败-下载成功- *  * DownloadManager:被观察者,有责任通知所有的观察者状态和进度发生变化 *  *  * 为了增强用户体验,加了数据库,存储每一次下载的文件信息, * 同时根据信息回显状态,下次再下载时端点续传。 *  * @author TCL * @date 2016-6-13 */public class DownloadManager {private static DownloadManager downloadManager;public static final int STATE_UNDO = 0;public static final int STATE_WAITTING = 1;public static final int STATE_DOWNLOADING = 2;public static final int STATE_PAUSE = 3;public static final int STATE_ERROR = 4;public static final int STATE_SUCCESS = 5;// 4.观察者集合private List observers = new ArrayList();// 下载队列private ConcurrentHashMap downloadInfoMap = new ConcurrentHashMap();// 下载任务的集合private ConcurrentHashMap downloadTaskMap = new ConcurrentHashMap();private DownloadManager() {}public static DownloadManager getInstance() {if (downloadManager == null) {synchronized (DownloadManager.class) {if (downloadManager == null) {downloadManager = new DownloadManager();}}}return downloadManager;}/** * 1.声明观察者接口 *  * @author TCL * @date 2016-6-13 */public interface DownloadObserver {// 下载状态发生变化public void onDownloadStateChanged(DownloadInfo downloadInfo);// 下载进度发生变化public void onDownloadProgressChanged(DownloadInfo downloadInfo);}// 2.注册观察者public void registerObserver(DownloadObserver observer) {if (observer != null && !observers.contains(observer)) {observers.add(observer);}}// 3.注销观察者public void unregisterObserver(DownloadObserver observer) {if (observer != null && observers.contains(observer)) {observers.remove(observer);}}// 5.通知下载状态发生变化public void notifyDownloadStateChanged(DownloadInfo downloadInfo) {for (DownloadObserver observer : observers) {observer.onDownloadStateChanged(downloadInfo);}}// 6.通知下载进度发生变化public void notifyDownloadProgressChanged(DownloadInfo downloadInfo) {for (DownloadObserver observer : observers) {observer.onDownloadProgressChanged(downloadInfo);}}// 下载的方法public synchronized void download(AppInfo appInfo) {// 如果对象是第一次下载的话,要创建一个新的DownloadInfo对象,重头下载// 如果之前下载过,要从数据库读取数据,接着下载,实现断点续传DownloadInfo downloadInfo = DownloadInfoDao.getInstance().getDownloadInfo(appInfo.id);if (downloadInfo == null) {downloadInfo = DownloadInfo.copy(appInfo);} //else {//downloadInfo.id = appInfo.id;//}LogUtils.i("download?name=" + downloadInfo.downloadUrl);downloadInfo.currentState = STATE_WAITTING;// 状态切换notifyDownloadStateChanged(downloadInfo);// 通知所有的观察者,状态发生变化了// 将下载对象放入下载队列集合downloadInfoMap.put(downloadInfo.id, downloadInfo);// 请求网络下载DownloadTask downloadTask = new DownloadTask(downloadInfo);ThreadManager.getInstance().execute(downloadTask);// 将下载任务放入集合中downloadTaskMap.put(downloadInfo.id, downloadTask);}class DownloadTask implements Runnable {private DownloadInfo downloadInfo;private HttpResult httpResult;public DownloadTask(DownloadInfo downloadInfo) {this.downloadInfo = downloadInfo;}@Overridepublic void run() {// 状态切换downloadInfo.currentState = STATE_DOWNLOADING;notifyDownloadStateChanged(downloadInfo);File file = new File(downloadInfo.path);if (!file.exists() || file.length() != downloadInfo.currentPos|| downloadInfo.currentPos == 0) {// 从头下载// 删除无效文件file.delete();// 文件不存在也是可以调用这个方法的,不报错downloadInfo.currentPos = 0;// 当前下载0LogUtils.i(HttpHelper.URL + "download?name="+ downloadInfo.downloadUrl);httpResult = HttpHelper.download(HttpHelper.URL+ "download?name=" + downloadInfo.downloadUrl);// 从头开始下载文件} else {// 断点续传// range表示服务器从文件的哪个位置开始返回数据httpResult = HttpHelper.download(HttpHelper.URL+ "download?name=" + downloadInfo.downloadUrl+ "&range=" + file.length());}if (httpResult != null && httpResult.getInputStream() != null) {InputStream inputStream = httpResult.getInputStream();BufferedOutputStream outputStream = null;try {outputStream = new BufferedOutputStream(new FileOutputStream(file, true));// 要在原文件追加int len = 0;byte[] buffer = new byte[1024];// 只有状态正在下载,才循环,解决下载中途暂停的问题while ((len = inputStream.read(buffer)) != -1&& downloadInfo.currentState == STATE_DOWNLOADING) {// 还要判断当前是否正在下载的状态outputStream.write(buffer, 0, len);outputStream.flush();// 更新下载进度downloadInfo.currentPos += len;notifyDownloadProgressChanged(downloadInfo);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {IOUtils.close(inputStream);IOUtils.close(outputStream);}// 文件下载结束,可能不成功if (file.length() == downloadInfo.size) {// 表示下载成功downloadInfo.currentState = STATE_SUCCESS;notifyDownloadStateChanged(downloadInfo);// install(downloadInfo);// 安装// 将下载大小,等信息存到数据库,以便于下次进入应用回显信息DownloadInfoDao.getInstance().saveDownloadInfo(downloadInfo);} else if (downloadInfo.currentState == STATE_PAUSE) {// 中途暂停// 暂停时信息存储DownloadInfoDao.getInstance().saveDownloadInfo(downloadInfo);notifyDownloadStateChanged(downloadInfo);} else {// 下载失败downloadInfo.currentState = STATE_ERROR;// 下载失败downloadInfo.currentPos = 0;notifyDownloadStateChanged(downloadInfo);file.delete();// 删除无效文件}} else {// 网络异常downloadInfo.currentState = STATE_ERROR;// 下载失败downloadInfo.currentPos = 0;notifyDownloadStateChanged(downloadInfo);file.delete();// 删除无效文件}// 从集合中移除下载任务downloadTaskMap.remove(downloadInfo.id);}}// 暂停的方法public synchronized void pause(AppInfo appInfo) {DownloadInfo downloadInfo = downloadInfoMap.get(appInfo.id);if (downloadInfo != null) {// 只有在正在下载和等待下载时才暂停if (downloadInfo.currentState == STATE_DOWNLOADING|| downloadInfo.currentState == STATE_WAITTING) {DownloadTask task = downloadTaskMap.get(downloadInfo.id);if (task != null) {ThreadManager.getInstance().cancel(task);}downloadInfo.currentState = STATE_PAUSE;// 状态切换notifyDownloadStateChanged(downloadInfo);// 通知切换// 暂停时把数据写到数据库DownloadInfoDao.getInstance().saveDownloadInfo(downloadInfo);}}}// 安装的方法public synchronized void install(DownloadInfo downloadInfo) {// 跳到系统的安装页面进行安装if (downloadInfo != null) {Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);LogUtils.e(downloadInfo.path + "---------");intent.setDataAndType(Uri.parse("file://" + downloadInfo.path),"application/vnd.android.package-archive");UIUtils.getContext().startActivity(intent);}}}

ThreadManager:

package com.study.googleplay.manager;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;import java.util.concurrent.TimeUnit;public class ThreadManager {/** * 线程池 *  * @author TCL * @date 2016-6-13 */private static ThreadPool threadPoolpool;public static ThreadPool getInstance() {if (threadPoolpool == null) {synchronized (ThreadManager.class) {if (threadPoolpool == null) {int threadCount = Runtime.getRuntime().availableProcessors() * 2 + 1;// 线程个数threadPoolpool = new ThreadPool(threadCount, threadCount,1L);}}}return threadPoolpool;}public static class ThreadPool {private int corePoolSize;// 核心线程数private int maximumPoolSize;// 最大线程数private long keepAliveTime;// 休息时间private ThreadPoolExecutor executor;private ThreadPool(int corePoolSize, int maximumPoolSize,long keepAliveTime) {this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.keepAliveTime = keepAliveTime;}public void execute(Runnable r) {// 参数1:核心线程数,参数2:最大线程数,参数3:休息时间,参数4:时间单位,// 参数5:线程队列参数6:生产线程的工厂,参数7:线程异常策略if (executor == null) {executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,new LinkedBlockingDeque(),Executors.defaultThreadFactory(), new AbortPolicy());}executor.execute(r);// 执行一个Runnable对象,具体执行时机由线程池说了算}// 取消任务public void cancel(Runnable r) {if (r != null) {// 如果任务还没开始,正在等待,可以通过 此方法移除,// 如果任务已经开始,需要在run方法中进行中断executor.getQueue().remove(r);// 从线程池中移除对象}}}}
Http的封装(HttpHelper):

package com.study.googleplay.http;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.StatusLine;import org.apache.http.client.HttpClient;import org.apache.http.client.HttpRequestRetryHandler;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpRequestBase;import org.apache.http.entity.ByteArrayEntity;import org.apache.http.impl.client.AbstractHttpClient;import org.apache.http.protocol.BasicHttpContext;import org.apache.http.protocol.HttpContext;import org.apache.http.protocol.SyncBasicHttpContext;import com.study.googleplay.utils.IOUtils;import com.study.googleplay.utils.LogUtils;import com.study.googleplay.utils.StringUtils;public class HttpHelper {public static final String URL = "http://127.0.0.1:8090/";/** get请求,获取返回字符串内容 */public static HttpResult get(String url) {HttpGet httpGet = new HttpGet(url);return execute(url, httpGet);}/** post请求,获取返回字符串内容 */public static HttpResult post(String url, byte[] bytes) {HttpPost httpPost = new HttpPost(url);ByteArrayEntity byteArrayEntity = new ByteArrayEntity(bytes);httpPost.setEntity(byteArrayEntity);return execute(url, httpPost);}/** 下载 */public static HttpResult download(String url) {HttpGet httpGet = new HttpGet(url);return execute(url, httpGet);}/** 执行网络访问 */private static HttpResult execute(String url, HttpRequestBase requestBase) {boolean isHttps = url.startsWith("https://");// 判断是否需要采用httpsAbstractHttpClient httpClient = HttpClientFactory.create(isHttps);HttpContext httpContext = new SyncBasicHttpContext(new BasicHttpContext());HttpRequestRetryHandler retryHandler = httpClient.getHttpRequestRetryHandler();// 获取重试机制int retryCount = 0;boolean retry = true;while (retry) {try {HttpResponse response = httpClient.execute(requestBase,httpContext);// 访问网络if (response != null) {return new HttpResult(response, httpClient, requestBase);}} catch (Exception e) {IOException ioException = new IOException(e.getMessage());retry = retryHandler.retryRequest(ioException, ++retryCount,httpContext);// 把错误异常交给重试机制,以判断是否需要采取从事LogUtils.e(e);}}return null;}/** http的返回结果的封装,可以直接从中获取返回的字符串或者流 */public static class HttpResult {private HttpResponse mResponse;private InputStream mIn;private String mStr;private HttpClient mHttpClient;private HttpRequestBase mRequestBase;public HttpResult(HttpResponse response, HttpClient httpClient,HttpRequestBase requestBase) {mResponse = response;mHttpClient = httpClient;mRequestBase = requestBase;}public int getCode() {StatusLine status = mResponse.getStatusLine();return status.getStatusCode();}/** 从结果中获取字符串,一旦获取,会自动关流,并且把字符串保存,方便下次获取 */public String getString() {if (!StringUtils.isEmpty(mStr)) {return mStr;}InputStream inputStream = getInputStream();ByteArrayOutputStream out = null;if (inputStream != null) {try {out = new ByteArrayOutputStream();byte[] buffer = new byte[1024 * 4];int len = -1;while ((len = inputStream.read(buffer)) != -1) {out.write(buffer, 0, len);}byte[] data = out.toByteArray();mStr = new String(data, "utf-8");} catch (Exception e) {LogUtils.e(e);} finally {IOUtils.close(out);close();}}return mStr;}/** 获取流,需要使用完毕后调用close方法关闭网络连接 */public InputStream getInputStream() {LogUtils.i("--------------------"+getCode());if (mIn == null && getCode() < 300) {HttpEntity entity = mResponse.getEntity();try {mIn = entity.getContent();} catch (Exception e) {LogUtils.e(e);}}return mIn;}/** 关闭网络连接 */public void close() {if (mRequestBase != null) {mRequestBase.abort();}IOUtils.close(mIn);if (mHttpClient != null) {mHttpClient.getConnectionManager().closeExpiredConnections();}}}}

下载信息的封装(DownloadInfo):

package com.study.googleplay.domain;import java.io.File;import android.os.Environment;import com.study.googleplay.manager.DownloadManager;/** * 下载对象   *  * @author TCL * @date 2016-6-13 */public class DownloadInfo {public String id;// 下载idpublic String name;// 下载名称public String downloadUrl;// 下载链接public String packageName;// 包名public long size;// 大小public long currentPos;// 当前下载位置public int currentState;// 当前下载状态public String path;// 下载到本地的路径public static final String GOOGLE_MARKET = "googlemarket";// sdcard跟目录文件夹名public static final String DOWNLOAD = "download";// 子文件夹名称,存放下载的文件// 获取下载进度(0-1)public float getProgress() {if (size == 0) {return 0;}return currentPos / (float) size;}// copy对象,从app对象中拷贝出一个DownloadInfopublic static DownloadInfo copy(AppInfo appInfo) {DownloadInfo downloadInfo = new DownloadInfo();downloadInfo.name = appInfo.name;downloadInfo.id = appInfo.id;downloadInfo.packageName = appInfo.packageName;downloadInfo.size = appInfo.size;downloadInfo.currentPos = 0;downloadInfo.currentState = DownloadManager.STATE_UNDO;downloadInfo.path = downloadInfo.getFilePath();return downloadInfo;}// 获取文件下载后存放路径public String getFilePath() {StringBuilder sb = new StringBuilder();String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath();sb.append(sdcard);sb.append(File.separator);sb.append(GOOGLE_MARKET);sb.append(File.separator);sb.append(DOWNLOAD);if (createDir(sb.toString())) {return sb.toString() + File.separator + name + ".apk";}return null;}public boolean createDir(String dir) {File dirFile = new File(dir);if (!dirFile.exists() || !dirFile.isDirectory()) {// 文件夹不存在或者不是一个文件夹return dirFile.mkdirs();}return true;// 文件夹存在}}


DownloadInfoDao:

package com.study.googleplay.db.dao;import android.content.ContentValues;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import com.study.googleplay.db.DownloadInfoOpenHelper;import com.study.googleplay.domain.DownloadInfo;import com.study.googleplay.utils.UIUtils;/** * 数据库访问类 *  * @author TCL * @date 2016-6-14 */public class DownloadInfoDao {private static DownloadInfoDao downloadInfoDao;private DownloadInfoOpenHelper downloadInfoOpenHelper;private DownloadInfoDao() {downloadInfoOpenHelper = new DownloadInfoOpenHelper(UIUtils.getContext());}public static DownloadInfoDao getInstance() {if (downloadInfoDao == null) {synchronized (DownloadInfoDao.class) {if (downloadInfoDao == null) {downloadInfoDao = new DownloadInfoDao();}}}return downloadInfoDao;}// 保存下载信息public boolean saveDownloadInfo(DownloadInfo downloadInfo) {SQLiteDatabase database = downloadInfoOpenHelper.getWritableDatabase();ContentValues values = new ContentValues();if (downloadInfo != null) {values.put("id", downloadInfo.id);values.put("name", downloadInfo.name);values.put("currentPos", downloadInfo.currentPos);values.put("path", downloadInfo.path);values.put("downloadUrl", downloadInfo.downloadUrl);values.put("size", downloadInfo.size);if (getDownloadInfo(downloadInfo.id) == null) {// 判断数据库是否已经存过database.insert("downloadInfo", null, values);} else {database.update("downloadInfo", values, "id = ?",new String[] { downloadInfo.id });}return true;}return false;}// 得到下载信息public DownloadInfo getDownloadInfo(String id) {SQLiteDatabase database = downloadInfoOpenHelper.getReadableDatabase();DownloadInfo downloadInfo = null;Cursor cursor = database.query("downloadInfo", null, "id = ? ",new String[] { id }, null, null, null);if (cursor.moveToNext()) {downloadInfo = new DownloadInfo();downloadInfo.id = id;downloadInfo.currentPos = cursor.getLong(cursor.getColumnIndex("currentPos"));downloadInfo.name = cursor.getString(cursor.getColumnIndex("name"));downloadInfo.path = cursor.getString(cursor.getColumnIndex("path"));downloadInfo.downloadUrl = cursor.getString(cursor.getColumnIndex("downloadUrl"));downloadInfo.size = cursor.getLong(cursor.getColumnIndex("size"));}return downloadInfo;}}

DownloadInfoOpenHelper:

package com.study.googleplay.db;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;/** * 存储下载信息的数据库 *  * @author TCL * @date 2016-6-14 */public class DownloadInfoOpenHelper extends SQLiteOpenHelper {public DownloadInfoOpenHelper(Context context) {super(context, "downloadInfoDb", null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table downloadInfo(id varchar(20),"+ "name varchar(20),currentPos number,"+ "path varchar(50),downloadUrl varchar(50),size number)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}


IOUtils:

package com.study.googleplay.utils;import java.io.Closeable;import java.io.IOException;public class IOUtils {/** 关闭流 */public static boolean close(Closeable io) {if (io != null) {try {io.close();} catch (IOException e) {LogUtils.e(e);}}return true;}}

LogUtils:

package com.study.googleplay.utils;import android.util.Log;public class LogUtils {/** 日志输出级别NONE */public static final int LEVEL_NONE = 0;/** 日志输出级别E */public static final int LEVEL_ERROR = 1;/** 日志输出级别W */public static final int LEVEL_WARN = 2;/** 日志输出级别I */public static final int LEVEL_INFO = 3;/** 日志输出级别D */public static final int LEVEL_DEBUG = 4;/** 日志输出级别V */public static final int LEVEL_VERBOSE = 5;/** 日志输出时的TAG */private static String mTag = "LogUtils";/** 是否允许输出log */private static int mDebuggable = LEVEL_VERBOSE;/** 以级别为 d 的形式输出LOG */public static void v(String msg) {if (mDebuggable >= LEVEL_VERBOSE) {Log.v(mTag, msg);}}/** 以级别为 d 的形式输出LOG */public static void d(String msg) {if (mDebuggable >= LEVEL_DEBUG) {Log.d(mTag, msg);}}/** 以级别为 i 的形式输出LOG */public static void i(String msg) {if (mDebuggable >= LEVEL_INFO) {Log.i(mTag, msg);}}/** 以级别为 w 的形式输出LOG */public static void w(String msg) {if (mDebuggable >= LEVEL_WARN) {Log.w(mTag, msg);}}/** 以级别为 w 的形式输出Throwable */public static void w(Throwable tr) {if (mDebuggable >= LEVEL_WARN) {Log.w(mTag, "", tr);}}/** 以级别为 w 的形式输出LOG信息和Throwable */public static void w(String msg, Throwable tr) {if (mDebuggable >= LEVEL_WARN && null != msg) {Log.w(mTag, msg, tr);}}/** 以级别为 e 的形式输出LOG */public static void e(String msg) {if (mDebuggable >= LEVEL_ERROR) {Log.e(mTag, msg);}}/** 以级别为 e 的形式输出Throwable */public static void e(Throwable tr) {if (mDebuggable >= LEVEL_ERROR) {Log.e(mTag, "", tr);}}/** 以级别为 e 的形式输出LOG信息和Throwable */public static void e(String msg, Throwable tr) {if (mDebuggable >= LEVEL_ERROR && null != msg) {Log.e(mTag, msg, tr);}}}
StringUtils:

package com.study.googleplay.utils;public class StringUtils {/** 判断字符串是否有值,如果为null或者是空字符串或者只有空格或者为"null"字符串,则返回true,否则则返回false */public static boolean isEmpty(String value) {if (value != null && !"".equalsIgnoreCase(value.trim())&& !"null".equalsIgnoreCase(value.trim())) {return false;} else {return true;}}}


更多相关文章

  1. 使用AnsyncTask异步类从网络上下载图片
  2. the android development environment
  3. Log控制输出
  4. 手机安全卫士开发系列(5)——自动更新
  5. 下载好自动安装
  6. VelocityTracker的用法
  7. 仿迅雷下载球
  8. android-使用AsyncTask做下载进度条
  9. Android(安卓)开发环境配置问题

随机推荐

  1. Android(安卓)电源管理
  2. android内存处理机制
  3. C#/mono开发Android应用程序入门(三)-平台
  4. Android逆向世界之一:smali文件
  5. 一个使用FFmpeg库读取3gp视频的例子-Andr
  6. 2019 年的 Android(安卓)网络 —— Retro
  7. Android的消息机制
  8. 【Android】蓝牙开发——BLE(低功耗蓝牙)(附
  9. Android事件分发机制完全解析,带你从源码
  10. iOS 7 需要再和 Android(安卓)比什么