实现思路:通过启动service,新建一个线程进行下载,handler通知到notification。
1、下载接口
public interface UpdateDownloadListener {
/**
* 下载请求开始回调
*/
public void onStarted();
/**
* 请求成功,下载前的准备回调
*
* @param contentLength
* 文件长度
* @param downloadUrl
* 下载地址
*/
public void onPrepared(long contentLength, String downloadUrl);
/**
* 进度更新回调
*
* @param progress
* @param downloadUrl
*/
public void onProgressChanged(int progress, String downloadUrl);
/**
* 下载过程中暂停的回调
*
* @param completeSize
* @param downloadUrl
*/
public void onPaused(int progress, int completeSize, String downloadUrl);
/**
* 下载完成回调
*
* @param completeSize
* @param downloadUrl
*/
public void onFinished(int completeSize, String downloadUrl);
/**
* 下载失败回调
*/
public void onFailure();
}
2、下载请求
public class UpdateDownloadRequest implements Runnable {
private int startPos = 0;
//下载路径
private String downloadUrl;
//文件保存路径
private String localFilePath;
//事件回调
private UpdateDownloadListener downloadListener;
private DownloadResponseHandler downloadHandler;
//下载的标志位
private boolean isDownloading = false;
//文件长度
private long contentLength;
public UpdateDownloadRequest(String downloadUrl, String localFilePath, UpdateDownloadListener downloadListener) {
this.downloadUrl = downloadUrl;
this.localFilePath = localFilePath;
this.downloadListener = downloadListener;
downloadHandler = new DownloadResponseHandler();
isDownloading = true;
}
//真正建立连接的方法
private void makeRequest() throws IOException, InterruptedException {
if (!Thread.currentThread().isInterrupted()) {
try {
URL url = new URL(downloadUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Range", "bytes=" + startPos + "-");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.connect();
contentLength = connection.getContentLength();
if (!Thread.currentThread().isInterrupted()) {
if (downloadHandler != null) {
downloadHandler.sendResponseMessage(connection.getInputStream());//取得与远程文件的流
}
}
} catch (IOException e) {
if (!Thread.currentThread().isInterrupted()) {
throw e;
}
}
}
}
@Override
public void run() {
try {
makeRequest();
} catch (IOException e) {
if (downloadHandler != null) {
downloadHandler.sendFailureMessage(FailureCode.IO);
}
} catch (InterruptedException e) {
if (downloadHandler != null) {
downloadHandler.sendFailureMessage(FailureCode.Interrupted);
}
}
}
public boolean isDownloading() {
return isDownloading;
}
public void stopDownloading() {
isDownloading = false;
}
/**
* 用来真正的去下载文件,并发送消息和回调接口
*/
public class DownloadResponseHandler {
protected static final int SUCCESS_MESSAGE = 0;
protected static final int FAILURE_MESSAGE = 1;
protected static final int START_MESSAGE = 2;
protected static final int FINISH_MESSAGE = 3;
protected static final int NETWORK_OFF = 4;
private Handler handler;
public DownloadResponseHandler() {
if (Looper.myLooper() != null) {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
handleSelfMessage(msg);
}
};
}
}
public void onFinish() {
downloadListener.onFinished(mCompleteSize, "");
}
public void onFailure(FailureCode failureCode) {
downloadListener.onFailure();
}
private void sendPausedMessage() {
sendMessage(obtainMessage(PAUSED_MESSAGE, null));
}
private void sendProgressChangedMessage(int progress) {
sendMessage(obtainMessage(PROGRESS_CHANGED, new Object[] { progress }));
}
protected void sendFailureMessage(FailureCode failureCode) {
sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[] { failureCode }));
}
//
// Pre-processing of messages (in original calling thread, typically the
// UI thread)
//
protected void handlePausedMessage() {
downloadListener.onPaused(progress, mCompleteSize, "");
}
protected void handleProgressChangedMessage(int progress) {
downloadListener.onProgressChanged(progress, "");
}
protected void handleFailureMessage(FailureCode failureCode) {
onFailure(failureCode);
}
protected void sendFinishMessage() {
sendMessage(obtainMessage(FINISH_MESSAGE, null));
}
// Methods which emulate android's Handler and Message methods
protected void handleSelfMessage(Message msg) {
Object[] response;
switch (msg.what) {
case FAILURE_MESSAGE:
response = (Object[]) msg.obj;
handleFailureMessage((FailureCode) response[0]);
break;
case PROGRESS_CHANGED:
response = (Object[]) msg.obj;
handleProgressChangedMessage(((Integer) response[0]).intValue());
break;
case PAUSED_MESSAGE:
handlePausedMessage();
break;
case FINISH_MESSAGE:
onFinish();
break;
}
}
protected void sendMessage(Message msg) {
if (handler != null) {
handler.sendMessage(msg);
} else {
handleSelfMessage(msg);
}
}
protected Message obtainMessage(int responseMessage, Object response) {
Message msg = null;
if (handler != null) {
msg = this.handler.obtainMessage(responseMessage, response);
} else {
msg = Message.obtain();
msg.what = responseMessage;
msg.obj = response;
}
return msg;
}
private String getTwoPointFloatStr(float value) {
DecimalFormat fnum = new DecimalFormat("0.00");
return fnum.format(value);
}
private int mCompleteSize = 0;
private int progress = 0;
private static final int PROGRESS_CHANGED = 5;
private static final int PAUSED_MESSAGE = 7;
//文件 下载方法,会发送各种类型的事件
void sendResponseMessage(InputStream is) {
RandomAccessFile randomAccessFile = null;
mCompleteSize = 0;
try {
byte[] buffer = new byte[1024];
int length = -1;
int limit = 0;
randomAccessFile = new RandomAccessFile(localFilePath, "rwd");
randomAccessFile.seek(startPos);
boolean isPaused = false;
while ((length = is.read(buffer)) != -1) {
if (isDownloading) {
randomAccessFile.write(buffer, 0, length);
mCompleteSize += length;
if ((startPos + mCompleteSize) < (contentLength + startPos)) {
progress = (int) (Float.parseFloat(getTwoPointFloatStr(
(float) (startPos + mCompleteSize) / (contentLength + startPos))) * 100);
if (limit % 30 == 0 &&progress <= 100) {
//为了显示我们notification的更新频率
sendProgressChangedMessage(progress); //在子线程中读取流数据,后转发到主线程中去。
}
}
limit++;
} else {
isPaused = true;
sendPausedMessage();
break;
}
}
stopDownloading();
if (!isPaused) {
sendFinishMessage();
}
} catch (IOException e) {
sendPausedMessage();
stopDownloading();
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
stopDownloading();
e.printStackTrace();
}
}
}
}
/**
* 失败码
*/
public enum FailureCode {
UnknownHost, Socket, SocketTimeout, ConnectTimeout, IO, HttpResponse, JSON, Interrupted
}
}
3、下载调度管理器
public class UpdateManager {
private static UpdateManager manager;
private ThreadPoolExecutor threadPool;
private UpdateDownloadRequest downloadRequest;
private UpdateManager() {
threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
}
static {
manager = new UpdateManager();
}
public static UpdateManager getInstance() {
return manager;
}
public void startDownload(String downloadUrl, String localFilePath,
UpdateDownloadListener downloadListener) {
if (downloadRequest != null && downloadRequest.isDownloading()) {
return;
}
checkLocalFilePath(localFilePath);
downloadRequest = new UpdateDownloadRequest(downloadUrl, localFilePath,
downloadListener);
Future<?> request = threadPool.submit(downloadRequest);
new WeakReference<Future<?>>(request);
}
//检查路径是否合法
private void checkLocalFilePath(String localFilePath) {
File path = new File(localFilePath.substring(0,
localFilePath.lastIndexOf("/") + 1));
File file = new File(localFilePath);
if (!path.exists()) {
path.mkdirs();
}
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4、下载的service
public class UpdateService extends Service {
/**
* 服务器固定地址
*/
private static final String APK_URL_TITLE = "http://www.imooc.com/mobile/mukewang.apk";
/**
* 文件存放路经
*/
private String filePath;
/**
* 文件下载地址
*/
private String apkUrl;
private NotificationManager notificationManager;
private Notification mNotification;
@Override
public void onCreate() {
notificationManager = (NotificationManager) getSystemService(android.content.Context.NOTIFICATION_SERVICE);
filePath = Environment.getExternalStorageDirectory() + "/imooc/imooc.apk";
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
apkUrl = APK_URL_TITLE;
notifyUser(getString(R.string.update_download_start), getString(R.string.update_download_start), 0);
startDownload();
return super.onStartCommand(intent, flags, startId);
}
private void startDownload() {
UpdateManager.getInstance().startDownload(apkUrl, filePath, new UpdateDownloadListener() {
@Override
public void onStarted() {
}
@Override
public void onProgressChanged(int progress, String downloadUrl) {
notifyUser(getString(R.string.update_download_processing),
getString(R.string.update_download_processing), progress);
}
@Override
public void onPrepared(long contentLength, String downloadUrl) {
}
@Override
public void onPaused(int progress, int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_failed),
getString(R.string.update_download_failed_msg), 0);
deleteApkFile();
stopSelf();// 停掉服务自身
}
@Override
public void onFinished(int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_finish), getString(R.string.update_download_finish),
100);
stopSelf();// 停掉服务自身
startActivity(getInstallApkIntent());
}
@Override
public void onFailure() {
notifyUser(getString(R.string.update_download_failed),
getString(R.string.update_download_failed_msg), 0);
deleteApkFile();
stopSelf();// 停掉服务自身
}
});
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void notifyUser(String tickerMsg, String message, int progress) {
notifyThatExceedLv21(tickerMsg, message, progress);
}
private void notifyThatExceedLv21(String tickerMsg, String message, int progress) {
NotificationCompat.Builder notification = new NotificationCompat.Builder(this);
notification.setSmallIcon(R.drawable.bg_message_imooc);
notification.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bg_message_imooc));
notification.setContentTitle(getString(R.string.app_name));
if (progress > 0 && progress < 100) {
notification.setProgress(100, progress, false);
} else {
/**
* 0,0,false,可以将进度条影藏
*/
notification.setProgress(0, 0, false);
notification.setContentText(message);
}
notification.setAutoCancel(true);
notification.setWhen(System.currentTimeMillis());
notification.setTicker(tickerMsg);
notification.setContentIntent(progress >= 100 ? getContentIntent() : PendingIntent.getActivity(this, 0,
new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
mNotification = notification.build();
notificationManager.notify(0, mNotification);
}
private PendingIntent getContentIntent() {
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, getInstallApkIntent(),
PendingIntent.FLAG_UPDATE_CURRENT);
return contentIntent;
}
/**
* 下载完成,安装
*/
private Intent getInstallApkIntent() {
File apkfile = new File(filePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
return intent;
}
/**
* 删除无用apk文件
*/
private boolean deleteApkFile() {
File apkFile = new File(filePath);
if (apkFile.exists() && apkFile.isFile()) {
return apkFile.delete();
}
return false;
}
}