android 广播通知 进度,Android下载进度监听和通知的处理详解

本文介绍了如何在Android应用中实现APK下载的进度监听和通知功能,包括DownloadCallback接口的使用、线程池管理、下载管理器封装,以及如何结合Notification和IntentService实现下载通知。重点展示了如何设置下载通知、断点续传及错误处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文实例为大家分享了Android下载进度监听和通知的具体代码,供大家参考,具体内容如下

下载管理器

关于下载进度的监听,这个比较简单,以apk文件下载为例,需要处理3个回调函数,分别是:

1.下载中

2.下载成功

3.下载失败

因此对应的回调接口就有了:

public interface DownloadCallback {

/**

* 下载成功

* @param file 目标文件

*/

void onComplete(File file);

/**

* 下载失败

* @param e

*/

void onError(Exception e);

/**

* 下载中

* @param count 总大小

* @param current 当前下载的进度

*/

void onLoading(long count, long current);

}

接下来就是线程池的管理了,当然你也可以直接使用Executors工具类中提供的几个静态方法来创建线程池,这里我是手动创建线程池的,代码如下:

public class ThreadManager {

private static ThreadPool mThreadPool;

/**

* 获取线程池

*

* @return

*/

public static ThreadPool getThreadPool() {

if (null == mThreadPool) {

synchronized (ThreadManager.class) {

if (null == mThreadPool) {

// cpu个数

int cpuNum = Runtime.getRuntime().availableProcessors();

//线程个数

int count = cpuNum * 2 + 1;

mThreadPool = new ThreadPool(count, count, 0);

}

}

}

return mThreadPool;

}

public static class ThreadPool {

int corePoolSize;// 核心线程数

int maximumPoolSize;// 最大线程数

long keepAliveTime;// 保持活跃时间(休息时间)

private ThreadPoolExecutor executor;

/**

* 构造方法初始化

*

* @param corePoolSize

* @param maximumPoolSize

* @param keepAliveTime

*/

private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) {

this.corePoolSize = corePoolSize;

this.maximumPoolSize = maximumPoolSize;

this.keepAliveTime = keepAliveTime;

}

private static final ThreadFactory sThreadFactory = new ThreadFactory() {

private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(Runnable r) {

return new Thread(r, "ThreadManager #" + mCount.getAndIncrement());

}

};

/**

* 执行线程任务

*

* @param r

*/

public void execute(Runnable r) {

//参1:核心线程数;参2:最大线程数;参3:保持活跃时间(休息时间);参4:活跃时间单位;参5:线程队列;参6:线程工厂;参7:异常处理策略

if (null == executor) {

executor = new ThreadPoolExecutor(corePoolSize,

maximumPoolSize,

keepAliveTime,

TimeUnit.SECONDS,

new LinkedBlockingQueue(),

sThreadFactory/*Executors.defaultThreadFactory()*/,

new ThreadPoolExecutor.AbortPolicy());

}

// 将当前Runnable对象放在线程池中执行

executor.execute(r);

}

/**

* 从线程池的任务队列中移除一个任务

* 如果当前任务已经是运行状态了,那么就表示不在任务队列中了,也就移除失败.

*/

public void cancle(Runnable r) {

if (null != executor && null != r) {

executor.getQueue().remove(r);

}

}

/**

* 是否关闭了线程池

* @return

*/

public boolean isShutdown(){

return executor.isShutdown();

}

/**

* 关闭线程池

*/

public void shutdown() {

executor.shutdown();

}

}

}

接下来就是一个下载管理器的封装了.

public class DownloadManager {

private DownloadCallback callback;

private Context context;

private String url;

private String fileName;

/**

* 初始化

* @param context 上下文

* @param url 目标文件url

* @param fileName 下载保存的文件名

* @param callback 下载回调函数

*/

public DownloadManager(final Context context, final String url, final String fileName, DownloadCallback callback) {

this.context = context;

this.url = url;

this.fileName = fileName;

this.callback = callback;

}

/**

* 开始下载

*/

public void startDownload() {

if (null == callback) return;

ThreadManager.getThreadPool().execute(new Runnable() {

@Override

public void run() {

HttpURLConnection conn = null;

try {

conn = (HttpURLConnection) new URL(url).openConnection();

conn.setRequestMethod("GET");

conn.setReadTimeout(5000);

conn.setConnectTimeout(10000);

long total = conn.getContentLength();

long curr = 0;

File file = new File(PathUtils.getDirectory(context, "apk"), fileName);

if (!file.exists()) {

file.createNewFile();

} else {

file.delete();

}

FileOutputStream fos = new FileOutputStream(file);

if (200 == conn.getResponseCode()) {

InputStream in = conn.getInputStream();

byte[] buff = new byte[1024];

int len = 0;

while ((len = in.read(buff)) != -1) {

fos.write(buff, 0, len);

curr += len;

callback.onLoading(total, curr);

}

in.close();

fos.close();

if (curr == total) {

callback.onComplete(file);

} else {

throw new Exception("curr != total");

}

} else {

throw new Exception("" + conn.getResponseCode());

}

} catch (Exception e) {

e.printStackTrace();

callback.onError(e);

} finally {

if (null != conn) {

conn.disconnect();

}

}

}

});

}

}

涉及的工具类如下:

PathUtils

public class PathUtils {

/**

* 获取cache目录下的rootDir目录

*

* @param context

* @param rootDir

* @return

*/

public static File getDirectory(Context context, String rootDir) {

String cachePath = context.getApplicationContext().getCacheDir().getAbsolutePath();

if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())

|| !Environment.isExternalStorageRemovable()) {

if (Build.VERSION.SDK_INT <= 8) {

cachePath = Environment.getExternalStorageDirectory().getAbsolutePath();

} else if (context.getApplicationContext().getExternalCacheDir() != null) {

cachePath = context.getApplicationContext().getExternalCacheDir().getAbsolutePath();

}

}

File rootF = new File(cachePath + File.separator + rootDir);

if (!rootF.exists()) {

rootF.mkdirs();

}

//修改目录权限可读可写可执行

String cmd = "chmod 777 -R " + rootF.getPath();

CmdUtils.execCmd(cmd);

return rootF;

}

}

CmdUtils

public class CmdUtils {

public static void execCmd(String cmd) {

try {

Runtime.getRuntime().exec(cmd);

} catch (IOException e) {

e.printStackTrace();

}

}

}

下载通知服务

同样以apk下载为例,要实现下载通知服务的话,就用到了Notification和Service,Notification用来通知下载进度并显示给用户看,Service用于后台默默的下载文件,这里我用到了IntentService,它的好处在于任务执行完毕后会自动关闭服务.同时程序用如果其他地方还想监听到下载的进度,那么可以在IntentService下载服务中通过发送广播告知进度.

ok,下面的代码可以直接用户实现apk的升级更新的操作.

public class UpdateService extends IntentService {

private File apkFile;

private String url;

private String fileName;

private NotificationCompat.Builder builderNotification;

private NotificationManager updateNotificationManager;

private int appNameID;

private int iconID;

private PendingIntent updatePendingIntent;

private boolean isUpdating;

public static final String ACTION_UPDATE_PROGRESS = "blog.youkuaiyun.com.mchenys.mobilesafe.ACTION_UPDATE_PROGRESS";

private Handler updateHandler = new Handler() {

public void handleMessage(Message msg) {

switch (msg.what) {

case 0:

UpdateService.this.onFailNotification();

break;

case 1:

UpdateService.this.downComplete();

break;

}

super.handleMessage(msg);

}

};

public UpdateService() {

super("UpdateService");

}

/**

* 开始更新

*

* @param context

* @param url 更新的url

* @param fileName 下载保存apk的文件名称

*/

public static void startUpdate(Context context, String url, String fileName) {

Intent intent = new Intent(context, UpdateService.class);

intent.putExtra("url", url);

intent.putExtra("fileName", fileName);

context.startService(intent);

}

@Override

protected void onHandleIntent(Intent intent) {

//WorkerThread

if (!this.isUpdating && intent != null) {

initData(intent);

initNotification();

downloadFile(true);

}

}

/**

* 初始数据

*

* @param intent

*/

private void initData(Intent intent) {

this.isUpdating = true;

this.url = intent.getStringExtra("url");

this.fileName = intent.getStringExtra("fileName");

this.apkFile = new File(PathUtils.getDirectory(getApplicationContext(), "mobilesafe"), fileName);

if (!this.apkFile.exists()) {

try {

this.apkFile.createNewFile();

} catch (IOException e) {

e.printStackTrace();

}

} else {

this.apkFile.delete();

}

this.appNameID = R.string.app_name;

this.iconID = R.mipmap.ic_logo;

}

/**

* 初始化通知

*/

private void initNotification() {

Intent updateCompletingIntent = new Intent();

updateCompletingIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

updateCompletingIntent.setClass(this.getApplication().getApplicationContext(), UpdateService.class);

this.updatePendingIntent = PendingIntent.getActivity(this, this.appNameID, updateCompletingIntent, PendingIntent.FLAG_CANCEL_CURRENT);

this.updateNotificationManager = (NotificationManager) this.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);

this.builderNotification = new NotificationCompat.Builder(this.getApplicationContext());

this.builderNotification.setSmallIcon(this.iconID).

setContentTitle(this.getResources().getString(this.appNameID)).

setContentIntent(updatePendingIntent).

setAutoCancel(true).

setTicker("开始更新").

setDefaults(1).

setProgress(100, 0, false).

setContentText("下载进度").

build();

this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());

}

/**

* 开始下载apk

*

* @param append 是否支持断点续传

*/

private void downloadFile(final boolean append) {

final Message message = updateHandler.obtainMessage();

int currentSize = 0; //上次下载的大小

long readSize = 0L;//已下载的总大小

long contentLength = 0;//服务器返回的数据长度

if (append) {

FileInputStream fis = null;

try {

fis = new FileInputStream(UpdateService.this.apkFile);

currentSize = fis.available();//获取上次下载的大小,断点续传可用

} catch (IOException e) {

e.printStackTrace();

} finally {

if (fis != null) {

try {

fis.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

HttpURLConnection conn = null;

InputStream is = null;

FileOutputStream fos = null;

try {

conn = (HttpURLConnection) new URL(UpdateService.this.url).openConnection();

conn.setRequestProperty("User-Agent", "Android");

if (currentSize > 0) {

conn.setRequestProperty("RANGE", "bytes=" + currentSize + "-");

}

conn.setConnectTimeout(10000);

conn.setReadTimeout(20000);

contentLength = conn.getContentLength();

if (conn.getResponseCode() == 404) {

throw new Exception("Cannot find remote file:" + UpdateService.this.url);

}

is = conn.getInputStream();

fos = new FileOutputStream(UpdateService.this.apkFile, append);

//实时更新通知

UpdateService.this.builderNotification.setSmallIcon(iconID).

setContentTitle(UpdateService.this.getResources().getString(UpdateService.this.appNameID)).

setContentIntent(updatePendingIntent).

setAutoCancel(true).

setTicker("正在更新").

setDefaults(0).

setContentText("下载进度").

build();

byte[] buffer = new byte[8*1024];

int len = 0;

while ((len = is.read(buffer)) != -1) {

fos.write(buffer, 0, len);

readSize += len;//累加已下载的大小

int progress = (int) (readSize * 100 / contentLength);

Log.d("UpdateService", (readSize * 100 / contentLength)+"");

if (progress % 8 == 0) {

UpdateService.this.builderNotification.setProgress(100, progress, false);

UpdateService.this.updateNotificationManager.notify(iconID, UpdateService.this.builderNotification.build());

//发送广播,通知外界下载进度

sendUpdateProgress(progress);

}

}

if (readSize == 0L) {

message.what = 0;

} else if (readSize == contentLength) {

message.what = 1;

sendUpdateProgress(100);

}

} catch (Exception e) {

e.printStackTrace();

message.what = 0;

} finally {

if (conn != null) {

conn.disconnect();

}

IOUtils.close(is);

IOUtils.close(fos);

updateHandler.sendMessage(message);

}

}

/**

* 发送更新进度

*

* @param progress

*/

private void sendUpdateProgress(int progress) {

Intent intent = new Intent(ACTION_UPDATE_PROGRESS);

intent.putExtra("progress", progress);

sendBroadcast(intent);

}

/**

* 下载失败通知用户重新下载

*/

private void onFailNotification() {

this.builderNotification.setSmallIcon(iconID).

setContentTitle("更新失败,请重新下载").

setContentIntent(updatePendingIntent).

setAutoCancel(true).

setTicker("更新失败").

setDefaults(1).

setProgress(100, 0, false).

setContentText("下载进度").

build();

this.updateNotificationManager.notify(iconID, this.builderNotification.build());

}

/**

* 下载完毕,后通知用户点击安装

*/

private void downComplete() {

UpdateService.this.isUpdating = false;

String cmd = "chmod 777 " + this.apkFile.getPath();

CmdUtils.execCmd(cmd);

Uri uri = Uri.fromFile(this.apkFile);

Intent updateCompleteIntent = new Intent("android.intent.action.VIEW");

updateCompleteIntent.addCategory("android.intent.category.DEFAULT");

updateCompleteIntent.setDataAndType(uri, "application/vnd.android.package-archive");

this.updatePendingIntent = PendingIntent.getActivity(this, this.appNameID, updateCompleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);

this.builderNotification.setSmallIcon(this.iconID).

setContentTitle(this.getResources().getString(this.appNameID)).

setContentIntent(this.updatePendingIntent).

setAutoCancel(true).

setTicker("更新完成").

setDefaults(1).

setProgress(0, 0, false).

setContentText("更新完成,点击安装").

build();

this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值