Android下载进度监听和通知的处理详解
程序员文章站
2023-12-01 17:22:46
本文实例为大家分享了android下载进度监听和通知的具体代码,供大家参考,具体内容如下
下载管理器
关于下载进度的监听,这个比较简单,以apk文件下载为例,需要处理3...
本文实例为大家分享了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<runnable>(), 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.csdn.net.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()); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。