溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android下載進(jìn)度監(jiān)聽和通知的處理詳解

發(fā)布時(shí)間:2020-10-05 11:46:05 來源:腳本之家 閱讀:224 作者:mChenys 欄目:移動(dòng)開發(fā)

本文實(shí)例為大家分享了Android下載進(jìn)度監(jiān)聽和通知的具體代碼,供大家參考,具體內(nèi)容如下

下載管理器

關(guān)于下載進(jìn)度的監(jiān)聽,這個(gè)比較簡(jiǎn)單,以apk文件下載為例,需要處理3個(gè)回調(diào)函數(shù),分別是:

1.下載中
2.下載成功
3.下載失敗

因此對(duì)應(yīng)的回調(diào)接口就有了:

public interface DownloadCallback {
  /**
   * 下載成功
   * @param file 目標(biāo)文件
   */
  void onComplete(File file);

  /**
   * 下載失敗
   * @param e
   */
  void onError(Exception e);

  /**
   * 下載中
   * @param count 總大小
   * @param current 當(dāng)前下載的進(jìn)度
   */
  void onLoading(long count, long current);
}

接下來就是線程池的管理了,當(dāng)然你也可以直接使用Executors工具類中提供的幾個(gè)靜態(tài)方法來創(chuàng)建線程池,這里我是手動(dòng)創(chuàng)建線程池的,代碼如下:

public class ThreadManager {

  private static ThreadPool mThreadPool;

  /**
   * 獲取線程池
   *
   * @return
   */
  public static ThreadPool getThreadPool() {
    if (null == mThreadPool) {
      synchronized (ThreadManager.class) {
        if (null == mThreadPool) {
          // cpu個(gè)數(shù)
          int cpuNum = Runtime.getRuntime().availableProcessors();
          //線程個(gè)數(shù)
          int count = cpuNum * 2 + 1;
          mThreadPool = new ThreadPool(count, count, 0);
        }
      }
    }
    return mThreadPool;
  }

  public static class ThreadPool {
    int corePoolSize;// 核心線程數(shù)
    int maximumPoolSize;// 最大線程數(shù)
    long keepAliveTime;// 保持活躍時(shí)間(休息時(shí)間)
    private ThreadPoolExecutor executor;

    /**
     * 構(gòu)造方法初始化
     *
     * @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());
      }
    };
    /**
     * 執(zhí)行線程任務(wù)
     *
     * @param r
     */
    public void execute(Runnable r) {
      //參1:核心線程數(shù);參2:最大線程數(shù);參3:保持活躍時(shí)間(休息時(shí)間);參4:活躍時(shí)間單位;參5:線程隊(duì)列;參6:線程工廠;參7:異常處理策略
      if (null == executor) {
        executor = new ThreadPoolExecutor(corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(),
            sThreadFactory/*Executors.defaultThreadFactory()*/,
            new ThreadPoolExecutor.AbortPolicy());
      }
      // 將當(dāng)前Runnable對(duì)象放在線程池中執(zhí)行
      executor.execute(r);
    }

    /**
     * 從線程池的任務(wù)隊(duì)列中移除一個(gè)任務(wù)
     * 如果當(dāng)前任務(wù)已經(jīng)是運(yùn)行狀態(tài)了,那么就表示不在任務(wù)隊(duì)列中了,也就移除失敗.
     */
    public void cancle(Runnable r) {
      if (null != executor && null != r) {
        executor.getQueue().remove(r);
      }
    }

    /**
     * 是否關(guān)閉了線程池
     * @return
     */
    public boolean isShutdown(){
      return executor.isShutdown();
    }

    /**
     * 關(guān)閉線程池
     */
    public void shutdown() {
      executor.shutdown();
    }
  }
}

接下來就是一個(gè)下載管理器的封裝了.

public class DownloadManager {

  private DownloadCallback callback;
  private Context context;
  private String url;
  private String fileName;

  /**
   * 初始化
   * @param context 上下文
   * @param url 目標(biāo)文件url
   * @param fileName 下載保存的文件名
   * @param callback 下載回調(diào)函數(shù)
   */
  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();
    }
    //修改目錄權(quán)限可讀可寫可執(zhí)行
    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();
    }
  }
}

下載通知服務(wù)

同樣以apk下載為例,要實(shí)現(xiàn)下載通知服務(wù)的話,就用到了Notification和Service,Notification用來通知下載進(jìn)度并顯示給用戶看,Service用于后臺(tái)默默的下載文件,這里我用到了IntentService,它的好處在于任務(wù)執(zhí)行完畢后會(huì)自動(dòng)關(guān)閉服務(wù).同時(shí)程序用如果其他地方還想監(jiān)聽到下載的進(jìn)度,那么可以在IntentService下載服務(wù)中通過發(fā)送廣播告知進(jìn)度.

ok,下面的代碼可以直接用戶實(shí)現(xiàn)apk的升級(jí)更新的操作.

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);
    }
  }

  /**
   * 初始數(shù)據(jù)
   *
   * @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("下載進(jìn)度").
        build();
    this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());
  }

  /**
   * 開始下載apk
   *
   * @param append 是否支持?jǐn)帱c(diǎn)續(xù)傳
   */
  private void downloadFile(final boolean append) {
    final Message message = updateHandler.obtainMessage();
    int currentSize = 0; //上次下載的大小
    long readSize = 0L;//已下載的總大小
    long contentLength = 0;//服務(wù)器返回的數(shù)據(jù)長(zhǎng)度
    if (append) {
      FileInputStream fis = null;
      try {
        fis = new FileInputStream(UpdateService.this.apkFile);
        currentSize = fis.available();//獲取上次下載的大小,斷點(diǎn)續(xù)傳可用
      } 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);
      //實(shí)時(shí)更新通知
      UpdateService.this.builderNotification.setSmallIcon(iconID).
          setContentTitle(UpdateService.this.getResources().getString(UpdateService.this.appNameID)).
          setContentIntent(updatePendingIntent).
          setAutoCancel(true).
          setTicker("正在更新").
          setDefaults(0).
          setContentText("下載進(jìn)度").
          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());
          //發(fā)送廣播,通知外界下載進(jìn)度
          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);
    }
  }

  /**
   * 發(fā)送更新進(jìn)度
   *
   * @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("更新失敗,請(qǐng)重新下載").
        setContentIntent(updatePendingIntent).
        setAutoCancel(true).
        setTicker("更新失敗").
        setDefaults(1).
        setProgress(100, 0, false).
        setContentText("下載進(jìn)度").
        build();
    this.updateNotificationManager.notify(iconID, this.builderNotification.build());
  }

  /**
   * 下載完畢,后通知用戶點(diǎn)擊安裝
   */
  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("更新完成,點(diǎn)擊安裝").
        build();
    this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());

  }
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI