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;
}
@Override
public 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;// 当前下载0
LogUtils.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);
}
}
}
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://");// 判断是否需要采用https
AbstractHttpClient 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;// 下载id
public 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对象中拷贝出一个DownloadInfo
public 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;
}
}
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);
}
@Override
public 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)");
}
@Override
public 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;
}
}
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;
}
}
}