Android下载进度监听和通知的处理详解

本文实例为大家分享了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());

  }
}

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

(0)

相关推荐

  • android中DownloadManager实现版本更新,监听下载进度实例

    DownloadManager简介 DownloadManager是Android 2.3(API level 9)用系统服务(Service)的方式提供了DownloadManager来处理长时间的下载操作.它包含两个静态内部类DownloadManager.Query(用来查询下载信息)和DownloadManager.Request(用来请求一个下载). DownloadManager主要提供了下面几个方法: public long enqueue(Request request)把任务加

  • Android之ProgressBar即时显示下载进度详解

    这里利用 ProgressBar 即时显示下载进度. 途中碰到的问题: 1.主线程中不能打开 URL,和只能在主线程中使用 Toast 等 2.子线程不能修改 UI 3.允许网络协议 4.暂停下载和继续下载   ........ fragment_main 布局文件 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.

  • Android 使用 DowanloadManager 实现下载并获取下载进度实例代码

    Android 使用 DowanloadManager 实现下载并获取下载进度实例代码 实现代码: package com.koolsee.gallery; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.app.DownloadManager; imp

  • Android文件下载进度条的实现代码

    main.xml: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_paren

  • Android实现文件下载进度显示功能

    和大家一起分享一下学习经验,如何实现Android文件下载进度显示功能,希望对广大初学者有帮助. 先上效果图: 上方的蓝色进度条,会根据文件下载量的百分比进行加载,中部的文本控件用来现在文件下载的百分比,最下方的ImageView用来展示下载好的文件,项目的目的就是动态向用户展示文件的下载量. 下面看代码实现:首先是布局文件: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xm

  • Android中使用AsyncTask做下载进度条实例代码

    android AsyncTask做下载进度条 AsyncTask是个不错的东西,可以使用它来做下载进度条.代码讲解如下: package com.example.downloadfile; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.a

  • Android实现Service下载文件,Notification显示下载进度的示例

    先放个gif..最终效果如果: 主要演示了Android从服务器下载文件,调用Notification显示下载进度,并且在下载完毕以后点击通知会跳转到安装APK的界面,演示是在真实的网络环境中使用真实的URL进行演示,来看看代码: MainActivity代码非常简单,就是启动一个Service: public class MainActivity extends AppCompatActivity { String download_url="http://shouji.360tpcdn.co

  • Android Retrofit文件下载进度显示问题的解决方法

    综述 在Retrofit2.0使用详解这篇文章中详细介绍了retrofit的用法.并且在retrofit中我们可以通过ResponseBody进行对文件的下载.但是在retrofit中并没有为我们提供显示下载进度的接口.在项目中,若是用户下载一个文件,无法实时给用户显示下载进度,这样用户的体验也是非常差的.那么下面就介绍一下在retrofit用于文件的下载如何实时跟踪下载进度. 演示 Retrofit文件下载进度更新的实现 在retrofit2.0中他依赖于Okhttp,所以如果我们需要解决这个

  • Android下载进度监听和通知的处理详解

    本文实例为大家分享了Android下载进度监听和通知的具体代码,供大家参考,具体内容如下 下载管理器 关于下载进度的监听,这个比较简单,以apk文件下载为例,需要处理3个回调函数,分别是: 1.下载中 2.下载成功 3.下载失败 因此对应的回调接口就有了: public interface DownloadCallback { /** * 下载成功 * @param file 目标文件 */ void onComplete(File file); /** * 下载失败 * @param e */

  • Ajax 文件上传进度监听之upload.onprogress案例详解

    $.ajax实现 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compati

  • Android TextWatcher三个回调以及监听EditText的输入案例详解

    TextWatcher是一个监听字符变化的类.当我们调用EditText的addTextChangedListener(TextWatcher)方法之后,就可以监听EditText的输入了. 在new出一个TextWatcher之后,我们需要实现三个抽象方法: beforeTextChanged onTextChanged afterTextChanged 看下beforeTextChanged的注释: This method is called to notify you that, with

  • Android ListView监听滑动事件的方法(详解)

    ListView的主要有两种滑动事件监听方法,OnTouchListener和OnScrollListener 1.OnTouchListener OnTouchListener方法来自View中的监听事件,可以在监听三个Action事件发生时通过MotionEvent的getX()方法或getY()方法获取到当前触摸的坐标值,来对用户的滑动方向进行判断,并可在不同的Action状态中做出相应的处理 mListView.setOnTouchListener(new View.OnTouchLis

  • Python脚本实现监听服务器的思路代码详解

    开前准备 Schedule使用方法. 基本的Linux操作 Python3环境 Step1 首先我得先假设你会了如何每十五分钟去运行一次检查这件事情.(后期我会补上如何去做定时任务,相信我!) 代码量比较少,选择在Linux环境下直接写脚本. import os #使用os的popen执行bash命令 content=os.popen("lsof -i:8080").read() 输出一下content看看,就是命令行执行输出的内容,看关键词webcache,但是输出的已经是文本文件了

  • Android监听home键的方法详解

    本文实例分析了Android监听home键的方法.分享给大家供大家参考,具体如下: 如何知道Home按钮被点击了呢?做launcher的时候,看源代码发现原因 如果你的Activity具备这些属性 <activity android:name="com.woyou.activity.HomeActivity" android:launchMode="singleInstance" > <intent-filter> <action an

  • Oracle监听口令及监听器安全详解

    很多Oracle用户都知道,Oracle的监听器一直存在着一个安全隐患,假如对此不设置安全措施,那么能够访问的用户就可以远程关闭监听器. 相关示例如下: D:>lsnrctl stop eygle LSNRCTL for 32-bit Windows: Version 10.2.0.3.0 - Production on 28-11月-2007 10:02:40 Copyright (c) 1991, 2006, Oracle. All rights reserved. 正在连接到 (DESCR

  • Bootstrap滚动监听(Scrollspy)插件详解

    滚动监听(Scrollspy)插件,即自动更新导航插件,会根据滚动条的位置自动更新对应的导航目标.其基本的实现是随着您的滚动,基于滚动条的位置向导航栏添加 .active class. 如果您想要单独引用该插件的功能,那么您需要引用 scrollspy.js.或者,正如 Bootstrap 插件概览 一章中所提到,您可以引用 bootstrap.js 或压缩版的 bootstrap.min.js. 一.用法 您可以向顶部导航添加滚动监听行为: 1.通过 data 属性:向您想要监听的元素(通常是

  • JavaFX 监听窗口关闭事件实例详解

    1.写在前面 在JavaFX的程序开发的时候,在使用多线程的时候,默认情况下在程序退出的时候,新开的线程依然在后台运行. 在这种情况下,可以监听窗口关闭事件,在里面关闭子线程. 2.具体实现的样例 package sample; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import java

  • javascript事件监听与事件委托实例详解

    本文实例讲述了javascript事件监听与事件委托.分享给大家供大家参考,具体如下: 事件监听与事件委托 在js中,常用到element.addEventListener()来进行事件的监听.但是当页面中存在大量需要绑定事件的元素时,这种方式可能会带来性能影响.此时,我们可以用事件委托的方式来进行事件的监听. 每个事件都经历三个阶段 捕获 到达目标 冒泡 事件委托需要用到事件的冒泡,冒泡就是事件发生时,上层会一层一层的接收这个事件. 如下页面结构: <body> <div id=&qu

随机推荐