最近在公司项目中为软件更新下载添加一个下载进度条,没想到本来应该是感觉很容易的事情,却花了我近1天的时间去搞定,特此记录一下,以防以后再遇到,同时希望能帮助那些遇到同意问题的同学们。首先我采用后台service异步下载
downIntent = new Intent(activity, DownloadService.class);
//传递下载地址
downIntent.putExtra("url", payload.getDownloadUrl());
//开启下载服务
activity.startService(downIntent);
下载服务的代码相对比较简单
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 创建文件路径
File tmpFile = new File(FILE_PATH);
if (!tmpFile.exists()) {
tmpFile.mkdir();
}
file = new File(FILE_PATH + FILE_NAME);
//获取通知管理者
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notif = new Notification();
notif.icon = R.drawable.logo;
notif.flags = Notification.FLAG_AUTO_CANCEL;
// 设置点击通知安装软件
Intent i = new Intent();
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setAction(android.content.Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
PendingIntent pIntent = PendingIntent.getActivity(this, 0, i, 0);
notif.contentIntent = pIntent;
// 显示通知
showNotification(0, true);
// 开启异步下载
DownAsyncTask task = new DownAsyncTask();
// 设置正在下载标志,防止重复下载
isDownLoad = true;
task.execute(intent.getStringExtra("url"));
return super.onStartCommand(intent, flags, startId);
}
到这里,代码都还是比较常见的,大家在网上一搜一大把。当我做了一个通知包含进度条,并且需要动态更改进度的通知时,问题出现了,通知一旦开启,手机基本马上卡死,知道下载完成。在网上找了很久,大家的解决方法基本就是采用每次更新通知重新实例化contentView。
// 显示进度条
private void showNotification(int progess, boolean isShow) {
//每次更新进度条都重新实例化contentView
notif.contentView = new RemoteViews(getPackageName(), R.layout.download_layout);
notif.contentView.setProgressBar(R.id.download_progress, 100, progess, isShow);
notif.contentView.setImageViewResource(R.id.download_logo, R.drawable.logo);
notif.contentView.setTextViewText(R.id.download_percent, progess + "%");
manager.notify(NOTIF_NUM, notif);
}
照着大家的方法改完后发现,情况的确有所好转,不过手机依然会在进度条进度到5%的时候卡死。
然后又百度了很久,自己也分析了很久,终于被一篇网友的博客所启发,找到了卡死的源头。
private int downloadCount = 0;
@Override
protected void onProgressUpdate(Double... values) {
int i = (int) ((values[0] / values[1]) * 100);
if (i >= 100) {
showNotification(100, false);
}
// 每隔3%的进度才更新一次通知,防止通知卡死进程
if (i - downloadCount >= 3) {
downloadCount = i;
showNotification(downloadCount, false);
}
}
在未优化之前,这里是直接调用显示通知方法的,这里有一个隐藏的问题,就是如果不加限制条件,而且网速又比较快的话,就会在短时间内,多次调用更新通知,从而造成手机卡死,我的解决方法很简单,只有当下载变化超过3%时才去更新通知。
最后附上完整下载service的源码
package com.boou.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import com.boou.goodlucklogisticscompany.R;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.widget.RemoteViews;
import android.widget.Toast;
@SuppressLint({ "SdCardPath", "ShowToast" })
public class DownloadService extends Service {
//下载的安装文件apk
public static File file;
//更新文件路径
private static final String FILE_PATH = "/sdcard/BoouUpdate";
//更新文件文件名
private static final String FILE_NAME = "/updata.apk";
// 是否处于下载状态
public static boolean isDownLoad = false;
// 通知管理器
private NotificationManager manager;
// 下载通知栏
private Notification notif;
//通知ID
private static final int NOTIF_NUM = 2013;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 创建文件路径
File tmpFile = new File(FILE_PATH);
if (!tmpFile.exists()) {
tmpFile.mkdir();
}
file = new File(FILE_PATH + FILE_NAME);
//获取通知管理者
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notif = new Notification();
notif.icon = R.drawable.logo;
notif.flags = Notification.FLAG_AUTO_CANCEL;
// 设置点击通知安装软件
Intent i = new Intent();
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setAction(android.content.Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
PendingIntent pIntent = PendingIntent.getActivity(this, 0, i, 0);
notif.contentIntent = pIntent;
// 显示通知
showNotification(0, true);
// 开启异步下载
DownAsyncTask task = new DownAsyncTask();
// 设置正在下载标志,防止重复下载
isDownLoad = true;
task.execute(intent.getStringExtra("url"));
return super.onStartCommand(intent, flags, startId);
}
// 显示进度条
private void showNotification(int progess, boolean isShow) {
//每次更新进度条都重新实例化contentView
notif.contentView = new RemoteViews(getPackageName(), R.layout.download_layout);
notif.contentView.setProgressBar(R.id.download_progress, 100, progess, isShow);
notif.contentView.setImageViewResource(R.id.download_logo, R.drawable.logo);
notif.contentView.setTextViewText(R.id.download_percent, progess + "%");
manager.notify(NOTIF_NUM, notif);
}
class DownAsyncTask extends AsyncTask {
@Override
protected File doInBackground(String... params) {
final String fileName = "updata.apk";
File tmpFile = new File("/sdcard/BoouUpdate");
if (!tmpFile.exists()) {
tmpFile.mkdir();
}
final File file = new File("/sdcard/BoouUpdate/" + fileName);
int num = 1;
try {
URL url = new URL(params[0]);
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[256];
conn.connect();
double count = 0;
if (conn.getResponseCode() >= 400) {
Toast.makeText(getApplicationContext(), "连接超时", 1000).show();
} else {
while (count <= 100) {
if (is != null) {
int numRead = is.read(buf);
if (numRead <= 0) {
break;
} else {
publishProgress(new Double[] { (double) (256 * num), (double) length });
fos.write(buf, 0, numRead);
num++;
}
} else {
break;
}
}
}
conn.disconnect();
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return file;
}
private int downloadCount = 0;
@Override
protected void onProgressUpdate(Double... values) {
int i = (int) ((values[0] / values[1]) * 100);
if (i >= 100) {
showNotification(100, false);
}
// 每隔3%的进度才更新一次通知,防止通知卡死进程
if (i - downloadCount >= 3) {
downloadCount = i;
showNotification(downloadCount, false);
}
}
@Override
protected void onPostExecute(File result) {
isDownLoad = false;
file = result;
// 询问安装
CheckUpdate.openAsk();
}
}
}