Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
这篇文章主要为大家详细介绍了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());
- }
- }
来源: http://www.phperz.com/article/17/0913/345874.html