欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android版多線程下載 仿下載助手(最新)

 更新時(shí)間:2015年08月05日 15:31:45   作者:u011733020  
我們都知道,下載助手,比如360,百度的 手機(jī)助手,下載APP的時(shí)候 ,都可以同時(shí)下載多個(gè),所以下載肯定是多線程的,所以我們就需要一個(gè)線程工具類來管理我們的線程,這個(gè)工具類的核心,就是線程池。接下來給大家介紹Android版多線程下載 仿下載助手(最新)

首先聲明一點(diǎn): 這里的多線程下載并不是指多個(gè)線程下載一個(gè) 文件,而是每個(gè)線程負(fù)責(zé)一個(gè)文件,今天給大家分享一個(gè)多線程下載的 例子。先看一下效果,點(diǎn)擊下載開始下載,同時(shí)顯示下載進(jìn)度,下載完成,變成程安裝,點(diǎn)擊安裝提示安裝應(yīng)用。

界面效果圖:

線程池ThreadPoolExecutor ,先簡單學(xué)習(xí)下這個(gè)線程池的使用

/** 
    * Parameters: 
     corePoolSize 
       the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set 
     maximumPoolSize 
       the maximum number of threads to allow in the pool 
     keepAliveTime 
       when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. 
     unit 
       the time unit for the keepAliveTime argument 
     workQueue 
       the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted          by the execute method. 
     handler 
       the handler to use when execution is blocked because the thread bounds and queue capacities are reached 
    Throws: 
     IllegalArgumentException - if one of the following holds: 
     corePoolSize < 0 
     keepAliveTime < 0 
     maximumPoolSize <= 0 
     maximumPoolSize < corePoolSize 
     NullPointerException - if workQueue or handler is null 
    */ 
   ThreadPoolExecutor threadpool=new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler) 

上面是 ThreadPoolExecutor的參數(shù)說明,
第一個(gè)參數(shù) corePoolSize : 空閑時(shí) 存在的線程數(shù)目、
第二個(gè)參數(shù) maximumPoolSize :允許同時(shí)存在的最大線程數(shù)、
第三個(gè)參數(shù) keepAliveTime: 這個(gè)參數(shù)是 允許空閑線程存活的時(shí)間、
第四個(gè)參數(shù) unit : 是 時(shí)間的單位 、
第五個(gè)參數(shù) workQueue :這個(gè)是一個(gè)容器,它里面存放的是、 threadpool.execute(new Runnable()) 執(zhí)行的線程.new Runnable()、
第六個(gè)參數(shù) handler:當(dāng)執(zhí)行被阻塞時(shí),該處理程序?qū)⒈蛔枞?,因?yàn)榫€程的邊界和隊(duì)列容量達(dá)到了 。
工具類 ThreadManager
介紹完了 線程池參數(shù),那我們就先創(chuàng)建一個(gè)線程管理的工具類 ThreadManager

public class ThreadManager { 
  public static final String DEFAULT_SINGLE_POOL_NAME = "DEFAULT_SINGLE_POOL_NAME"; 
  private static ThreadPoolProxy mLongPool = null; 
  private static Object mLongLock = new Object(); 
  private static ThreadPoolProxy mShortPool = null; 
  private static Object mShortLock = new Object(); 
  private static ThreadPoolProxy mDownloadPool = null; 
  private static Object mDownloadLock = new Object(); 
  private static Map<String, ThreadPoolProxy> mMap = new HashMap<String, ThreadPoolProxy>(); 
  private static Object mSingleLock = new Object(); 
  /** 獲取下載線程 */ 
  public static ThreadPoolProxy getDownloadPool() { 
    synchronized (mDownloadLock) { 
      if (mDownloadPool == null) { 
        mDownloadPool = new ThreadPoolProxy(3, 3, 5L); 
      } 
      return mDownloadPool; 
    } 
  } 
  /** 獲取一個(gè)用于執(zhí)行長耗時(shí)任務(wù)的線程池,避免和短耗時(shí)任務(wù)處在同一個(gè)隊(duì)列而阻塞了重要的短耗時(shí)任務(wù),通常用來聯(lián)網(wǎng)操作 */ 
  public static ThreadPoolProxy getLongPool() { 
    synchronized (mLongLock) { 
      if (mLongPool == null) { 
        mLongPool = new ThreadPoolProxy(5, 5, 5L); 
      } 
      return mLongPool; 
    } 
  } 
  /** 獲取一個(gè)用于執(zhí)行短耗時(shí)任務(wù)的線程池,避免因?yàn)楹秃臅r(shí)長的任務(wù)處在同一個(gè)隊(duì)列而長時(shí)間得不到執(zhí)行,通常用來執(zhí)行本地的IO/SQL */ 
  public static ThreadPoolProxy getShortPool() { 
    synchronized (mShortLock) { 
      if (mShortPool == null) { 
        mShortPool = new ThreadPoolProxy(2, 2, 5L); 
      } 
      return mShortPool; 
    } 
  } 
  /** 獲取一個(gè)單線程池,所有任務(wù)將會(huì)被按照加入的順序執(zhí)行,免除了同步開銷的問題 */ 
  public static ThreadPoolProxy getSinglePool() { 
    return getSinglePool(DEFAULT_SINGLE_POOL_NAME); 
  } 
  /** 獲取一個(gè)單線程池,所有任務(wù)將會(huì)被按照加入的順序執(zhí)行,免除了同步開銷的問題 */ 
  public static ThreadPoolProxy getSinglePool(String name) { 
    synchronized (mSingleLock) { 
      ThreadPoolProxy singlePool = mMap.get(name); 
      if (singlePool == null) { 
        singlePool = new ThreadPoolProxy(1, 1, 5L); 
        mMap.put(name, singlePool); 
      } 
      return singlePool; 
    } 
  } 
  public static class ThreadPoolProxy { 
    private ThreadPoolExecutor mPool; 
    private int mCorePoolSize; 
    private int mMaximumPoolSize; 
    private long mKeepAliveTime; 
    private ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) { 
      mCorePoolSize = corePoolSize; 
      mMaximumPoolSize = maximumPoolSize; 
      mKeepAliveTime = keepAliveTime; 
    } 
    /** 執(zhí)行任務(wù),當(dāng)線程池處于關(guān)閉,將會(huì)重新創(chuàng)建新的線程池 */ 
    public synchronized void execute(Runnable run) { 
      if (run == null) { 
        return; 
      } 
      if (mPool == null || mPool.isShutdown()) { 
        mPool = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new AbortPolicy()); 
      } 
      mPool.execute(run); 
    } 
    /** 取消線程池中某個(gè)還未執(zhí)行的任務(wù) */ 
    public synchronized void cancel(Runnable run) { 
      if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { 
        mPool.getQueue().remove(run); 
      } 
    } 
    /** 取消線程池中某個(gè)還未執(zhí)行的任務(wù) */ 
    public synchronized boolean contains(Runnable run) { 
      if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { 
        return mPool.getQueue().contains(run); 
      } else { 
        return false; 
      } 
    } 
    /** 立刻關(guān)閉線程池,并且正在執(zhí)行的任務(wù)也將會(huì)被中斷 */ 
    public void stop() { 
      if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { 
        mPool.shutdownNow(); 
      } 
    } 
    /** 平緩關(guān)閉單任務(wù)線程池,但是會(huì)確保所有已經(jīng)加入的任務(wù)都將會(huì)被執(zhí)行完畢才關(guān)閉 */ 
    public synchronized void shutdown() { 
      if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { 
        mPool.shutdownNow(); 
      } 
    } 
  } 
} 

這個(gè)線程池工具類 主要就是 生成一個(gè)線程池, 以及 取消線程池中的任務(wù),查詢線程池中是否包含某一任務(wù)。
下載任務(wù) DownloadTask
我們的現(xiàn)在線程 DownloadTask 就 通過 ThreadManager .getDownloadPool().execute() 方法 交給線程池去管理。
有了線程池管理我們的線程, 那我們下一步 就是 DownloadTask 這個(gè)類去下載了。

/** 下載任務(wù) */ 
 public class DownloadTask implements Runnable { 
   private DownloadInfo info; 
   public DownloadTask(DownloadInfo info) { 
     this.info = info; 
   } 
   @Override 
   public void run() { 
     info.setDownloadState(STATE_DOWNLOADING);// 先改變下載狀態(tài) 
     notifyDownloadStateChanged(info); 
     File file = new File(info.getPath());// 獲取下載文件 
     HttpResult httpResult = null; 
     InputStream stream = null; 
     if (info.getCurrentSize() == 0 || !file.exists() 
         || file.length() != info.getCurrentSize()) { 
       // 如果文件不存在,或者進(jìn)度為0,或者進(jìn)度和文件長度不相符,就需要重新下載 
       info.setCurrentSize(0); 
       file.delete(); 
     } 
     httpResult = HttpHelper.download(info.getUrl()); 
     // else { 
     // // //文件存在且長度和進(jìn)度相等,采用斷點(diǎn)下載 
     // httpResult = HttpHelper.download(info.getUrl() + "&range=" + 
     // info.getCurrentSize()); 
     // } 
     if (httpResult == null 
         || (stream = httpResult.getInputStream()) == null) { 
       info.setDownloadState(STATE_ERROR);// 沒有下載內(nèi)容返回,修改為錯(cuò)誤狀態(tài) 
       notifyDownloadStateChanged(info); 
     } else { 
       try { 
         skipBytesFromStream(stream, info.getCurrentSize()); 
       } catch (Exception e1) { 
         e1.printStackTrace(); 
       } 
       FileOutputStream fos = null; 
       try { 
         fos = new FileOutputStream(file, true); 
         int count = -1; 
         byte[] buffer = new byte[1024]; 
         while (((count = stream.read(buffer)) != -1) 
             && info.getDownloadState() == STATE_DOWNLOADING) { 
           // 每次讀取到數(shù)據(jù)后,都需要判斷是否為下載狀態(tài),如果不是,下載需要終止,如果是,則刷新進(jìn)度 
           fos.write(buffer, 0, count); 
           fos.flush(); 
           info.setCurrentSize(info.getCurrentSize() + count); 
           notifyDownloadProgressed(info);// 刷新進(jìn)度 
         } 
       } catch (Exception e) { 
         info.setDownloadState(STATE_ERROR); 
         notifyDownloadStateChanged(info); 
         info.setCurrentSize(0); 
         file.delete(); 
       } finally { 
         IOUtils.close(fos); 
         if (httpResult != null) { 
           httpResult.close(); 
         } 
       } 
       // 判斷進(jìn)度是否和app總長度相等 
       if (info.getCurrentSize() == info.getAppSize()) { 
         info.setDownloadState(STATE_DOWNLOADED); 
         notifyDownloadStateChanged(info); 
       } else if (info.getDownloadState() == STATE_PAUSED) {// 判斷狀態(tài) 
         notifyDownloadStateChanged(info); 
       } else { 
         info.setDownloadState(STATE_ERROR); 
         notifyDownloadStateChanged(info); 
         info.setCurrentSize(0);// 錯(cuò)誤狀態(tài)需要?jiǎng)h除文件 
         file.delete(); 
       } 
     } 
     mTaskMap.remove(info.getId()); 
   } 
 } 

下載的原理 很簡單,就是通過目標(biāo)的URL 拿到流,然后寫到本地。
因?yàn)橄螺d在 run()里面執(zhí)行,這個(gè)DownloadTask 類 我們就看run() 方法的實(shí)現(xiàn),所以 關(guān)鍵代碼 就是下面一點(diǎn)點(diǎn)

fos = new FileOutputStream(file, true); 
     int count = -1; 
     byte[] buffer = new byte[1024]; 
     while (((count = stream.read(buffer)) != -1) 
         && info.getDownloadState() == STATE_DOWNLOADING) { 
       // 每次讀取到數(shù)據(jù)后,都需要判斷是否為下載狀態(tài),如果不是,下載需要終止,如果是,則刷新進(jìn)度 
       fos.write(buffer, 0, count); 
       fos.flush(); 
       info.setCurrentSize(info.getCurrentSize() + count); 
       notifyDownloadProgressed(info);// 刷新進(jìn)度 
     } 

這個(gè)在我們剛接觸Java 的時(shí)候 肯定都寫過了。 這就是往本地寫數(shù)據(jù)的代碼。所以run()方法中的 前面 就是拿到 stream 輸入流, 以及 把file 創(chuàng)建出來。
刷新進(jìn)度,狀態(tài)
關(guān)于控制 button中text 顯示 暫停 ,下載,還是進(jìn)度,就靠 notifyDownloadProgressed(info);和 notifyDownloadStateChanged(info)兩個(gè)方法, 這兩個(gè)方法 實(shí)際上調(diào)用的是兩個(gè)接口,只要我們?cè)谖覀冃枰淖兘缑娴念惱?實(shí)現(xiàn)這兩個(gè)接口,就可以接收到 包含最新信息的info對(duì)象。而我們?cè)谀膫€(gè)類里改變button 上面 顯示的文字呢? 當(dāng)然是在 我們的adapter 里面了,大家都知道 是在 adapter 的getView() 方法里面 加載的每一條數(shù)據(jù)的布局。
那就一起看下是不是這樣子呢?

public class RecommendAdapter extends BaseAdapter implements 
    DownloadManager.DownloadObserver { 
  ArrayList<AppInfo> list; 
  private List<ViewHolder> mDisplayedHolders; 
  private FinalBitmap finalBitmap; 
  private Context context; 
  public RecommendAdapter(ArrayList<AppInfo> list, FinalBitmap finalBitmap, 
      Context context) { 
    this.list = list; 
    this.context = context; 
    this.finalBitmap = finalBitmap; 
    mDisplayedHolders = new ArrayList<ViewHolder>(); 
  } 
 
  public void startObserver() { 
    DownloadManager.getInstance().registerObserver(this); 
  } 
  public void stopObserver() { 
    DownloadManager.getInstance().unRegisterObserver(this); 
  } 
  @Override 
  public int getCount() { 
    return list.size(); 
  } 
  @Override 
  public Object getItem(int position) { 
    return list.get(position); 
  } 
  @Override 
  public long getItemId(int position) { 
    return position; 
  } 
  @Override 
  public View getView(int position, View convertView, ViewGroup parent) { 
    final AppInfo appInfo = list.get(position); 
    final ViewHolder holder; 
    if (convertView == null) { 
      holder = new ViewHolder(context); 
    } else { 
      holder = (ViewHolder) convertView.getTag(); 
    } 
    holder.setData(appInfo); 
    mDisplayedHolders.add(holder); 
    return holder.getRootView(); 
  } 
  @Override 
  public void onDownloadStateChanged(DownloadInfo info) { 
    refreshHolder(info); 
  } 
  @Override 
  public void onDownloadProgressed(DownloadInfo info) { 
    refreshHolder(info); 
  } 
  public List<ViewHolder> getDisplayedHolders() { 
    synchronized (mDisplayedHolders) { 
      return new ArrayList<ViewHolder>(mDisplayedHolders); 
    } 
  } 
  public void clearAllItem() { 
    if (list != null){ 
      list.clear(); 
    } 
    if (mDisplayedHolders != null) { 
      mDisplayedHolders.clear(); 
    } 
  } 
  public void addItems(ArrayList<AppInfo> infos) { 
    list.addAll(infos); 
  } 
  private void refreshHolder(final DownloadInfo info) { 
    List<ViewHolder> displayedHolders = getDisplayedHolders(); 
    for (int i = 0; i < displayedHolders.size(); i++) { 
      final ViewHolder holder = displayedHolders.get(i); 
      AppInfo appInfo = holder.getData(); 
      if (appInfo.getId() == info.getId()) { 
        AppUtil.post(new Runnable() { 
          @Override 
          public void run() { 
            holder.refreshState(info.getDownloadState(), 
                info.getProgress()); 
          } 
        }); 
      } 
    } 
  } 
  public class ViewHolder { 
    public TextView textView01; 
    public TextView textView02; 
    public TextView textView03; 
    public TextView textView04; 
    public ImageView imageView_icon; 
    public Button button; 
    public LinearLayout linearLayout; 
    public AppInfo mData; 
    private DownloadManager mDownloadManager; 
    private int mState; 
    private float mProgress; 
    protected View mRootView; 
    private Context context; 
    private boolean hasAttached; 
    public ViewHolder(Context context) { 
      mRootView = initView(); 
      mRootView.setTag(this); 
      this.context = context; 
 
    } 
    public View getRootView() { 
      return mRootView; 
    } 
    public View initView() { 
      View view = AppUtil.inflate(R.layout.item_recommend_award); 
      imageView_icon = (ImageView) view 
          .findViewById(R.id.imageview_task_app_cion); 
      textView01 = (TextView) view 
          .findViewById(R.id.textview_task_app_name); 
      textView02 = (TextView) view 
          .findViewById(R.id.textview_task_app_size); 
      textView03 = (TextView) view 
          .findViewById(R.id.textview_task_app_desc); 
      textView04 = (TextView) view 
          .findViewById(R.id.textview_task_app_love); 
      button = (Button) view.findViewById(R.id.button_task_download); 
      linearLayout = (LinearLayout) view 
          .findViewById(R.id.linearlayout_task); 
      button.setOnClickListener(new OnClickListener() { 
        @Override 
        public void onClick(View v) { 
          System.out.println("mState:173  "+mState); 
          if (mState == DownloadManager.STATE_NONE 
              || mState == DownloadManager.STATE_PAUSED 
              || mState == DownloadManager.STATE_ERROR) { 
            mDownloadManager.download(mData); 
          } else if (mState == DownloadManager.STATE_WAITING 
              || mState == DownloadManager.STATE_DOWNLOADING) { 
            mDownloadManager.pause(mData); 
          } else if (mState == DownloadManager.STATE_DOWNLOADED) { 
//           tell2Server(); 
            mDownloadManager.install(mData); 
          } 
        } 
      }); 
      return view; 
    } 
 
    public void setData(AppInfo data) { 
      if (mDownloadManager == null) { 
        mDownloadManager = DownloadManager.getInstance(); 
      } 
       String filepath= FileUtil.getDownloadDir(AppUtil.getContext()) + File.separator + data.getName() + ".apk"; 
        boolean existsFile = FileUtil.isExistsFile(filepath); 
        if(existsFile){ 
          int fileSize = FileUtil.getFileSize(filepath); 
          if(data.getSize()==fileSize){ 
            DownloadInfo downloadInfo = DownloadInfo.clone(data); 
            downloadInfo.setCurrentSize(data.getSize()); 
            downloadInfo.setHasFinished(true); 
            mDownloadManager.setDownloadInfo(data.getId(),downloadInfo ); 
          } 
//         else if(fileSize>0){ 
//           DownloadInfo downloadInfo = DownloadInfo.clone(data); 
//           downloadInfo.setCurrentSize(data.getSize()); 
//           downloadInfo.setHasFinished(false); 
//           mDownloadManager.setDownloadInfo(data.getId(),downloadInfo ); 
//         } 
        } 
      DownloadInfo downloadInfo = mDownloadManager.getDownloadInfo(data 
          .getId()); 
      if (downloadInfo != null) { 
        mState = downloadInfo.getDownloadState(); 
        mProgress = downloadInfo.getProgress(); 
      } else { 
        mState = DownloadManager.STATE_NONE; 
        mProgress = 0; 
      } 
      this.mData = data; 
      refreshView(); 
    } 
    public AppInfo getData() { 
      return mData; 
    } 
    public void refreshView() { 
      linearLayout.removeAllViews(); 
      AppInfo info = getData(); 
      textView01.setText(info.getName()); 
      textView02.setText(FileUtil.FormetFileSize(info.getSize())); 
      textView03.setText(info.getDes()); 
      textView04.setText(info.getDownloadNum() + "下載量); 
      finalBitmap.display(imageView_icon, info.getIconUrl()); 
 
      if (info.getType().equals("0")) { 
//       mState = DownloadManager.STATE_READ; 
        textView02.setVisibility(View.GONE); 
      }else{ 
        String path=FileUtil.getDownloadDir(AppUtil.getContext()) + File.separator + info.getName() + ".apk"; 
        hasAttached = FileUtil.isValidAttach(path, false); 
        DownloadInfo downloadInfo = mDownloadManager.getDownloadInfo(info 
            .getId()); 
        if (downloadInfo != null && hasAttached) { 
          if(downloadInfo.isHasFinished()){ 
            mState = DownloadManager.STATE_DOWNLOADED; 
          }else{ 
            mState = DownloadManager.STATE_PAUSED; 
          } 
        } else { 
          mState = DownloadManager.STATE_NONE; 
          if(downloadInfo !=null){ 
            downloadInfo.setDownloadState(mState); 
          } 
        } 
      } 
      refreshState(mState, mProgress); 
    } 
    public void refreshState(int state, float progress) { 
      mState = state; 
      mProgress = progress; 
      switch (mState) { 
      case DownloadManager.STATE_NONE: 
        button.setText(R.string.app_state_download); 
        break; 
      case DownloadManager.STATE_PAUSED: 
        button.setText(R.string.app_state_paused); 
        break; 
      case DownloadManager.STATE_ERROR: 
        button.setText(R.string.app_state_error); 
        break; 
      case DownloadManager.STATE_WAITING: 
        button.setText(R.string.app_state_waiting); 
        break; 
      case DownloadManager.STATE_DOWNLOADING: 
        button.setText((int) (mProgress * 100) + "%"); 
        break; 
      case DownloadManager.STATE_DOWNLOADED: 
        button.setText(R.string.app_state_downloaded); 
        break; 
//     case DownloadManager.STATE_READ: 
//       button.setText(R.string.app_state_read); 
//       break; 
      default: 
        break; 
      } 
    } 
  } 
} 

何時(shí) 注冊(cè) 監(jiān)聽observer
里面代碼有點(diǎn)多,那就看startObserver()方法做了什么。

public void startObserver() { 
    DownloadManager.getInstance().registerObserver(this); 
  } 

這里 是 注冊(cè)了observer, Observer 是什么東西?在DownloadManager 中我們定義了


public interface DownloadObserver { 
  public void onDownloadStateChanged(DownloadInfo info); 
  public void onDownloadProgressed(DownloadInfo info); 
} 

一個(gè)接口,里面有兩個(gè)抽象方法 一個(gè)是 進(jìn)度,另一個(gè)是下載狀態(tài)。
那回過頭來,屢一下, 我們?cè)?下載的關(guān)鍵代碼里面調(diào)用了
DownloadObserver onDownloadProgressed()
DownloadObserver.onDownloadStateChanged()

兩個(gè)抽象方法,而我們?cè)?adapter

@Override 
 public void onDownloadStateChanged(DownloadInfo info) { 
   refreshHolder(info); 
 } 
 @Override 
 public void onDownloadProgressed(DownloadInfo info) { 
   refreshHolder(info); 
 } 

中實(shí)現(xiàn)了 這兩個(gè)方法 就可以輕松的控制 去 刷新 和改變 下載狀態(tài)了。
細(xì)心的朋友 或許 發(fā)現(xiàn)問題了,對(duì),我們還沒有注冊(cè)O(shè)bserver,就在 DownloadManager 中去調(diào)用了。
這里 在看下DownloadManager 中 調(diào)用的方法
/

** 當(dāng)下載狀態(tài)發(fā)送改變的時(shí)候回調(diào) */ 
public void notifyDownloadStateChanged(DownloadInfo info) { 
  synchronized (mObservers) { 
    for (DownloadObserver observer : mObservers) { 
      observer.onDownloadStateChanged(info); 
    } 
  } 
} 
/** 當(dāng)下載進(jìn)度發(fā)送改變的時(shí)候回調(diào) */ 
public void notifyDownloadProgressed(DownloadInfo info) { 
  synchronized (mObservers) { 
    for (DownloadObserver observer : mObservers) { 
      observer.onDownloadProgressed(info); 
    } 
  } 
} 

是的,這里我們遍歷一個(gè)observer 容器,然后去刷新 ,所以我們還需要 把 Observer 對(duì)象 添加到 集合 mObservers 中,
所以肯定有這樣一個(gè)方法 講 observer 添加到集合中 。

/* 注冊(cè)觀察者 /  
public void registerObserver(DownloadObserver observer) {  
synchronized (mObservers) {  
if (!mObservers.contains(observer)) {  
mObservers.add(observer);  
}  
}  
} 
[java] view plaincopy
/** 反注冊(cè)觀察者 */ 
public void unRegisterObserver(DownloadObserver observer) { 
  synchronized (mObservers) { 
    if (mObservers.contains(observer)) { 
      mObservers.remove(observer); 
    } 
  } 
} 

所以最后一步,因?yàn)?adapter 方法中有 startObserver, 所以 我們?cè)?主界面 MainActivity 的類中調(diào)用 adapter.startObser() 將 實(shí)現(xiàn)了 接口的adapter 對(duì)象 添加到 Observer 容器中 就可以了。
OK。大功告成!
=============================================
DownloadManager 代碼
這里 貼一下DownloadManager 代碼

public class DownloadManager { 
  public static final int STATE_NONE = 0; 
  /** 等待中 */ 
  public static final int STATE_WAITING = 1; 
  /** 下載中 */ 
  public static final int STATE_DOWNLOADING = 2; 
  /** 暫停 */ 
  public static final int STATE_PAUSED = 3; 
  /** 下載完畢 */ 
  public static final int STATE_DOWNLOADED = 4; 
  /** 下載失敗 */ 
  public static final int STATE_ERROR = 5; 
  // public static final int STATE_READ = 6; 
  private static DownloadManager instance; 
  private DownloadManager() { 
  } 
  /** 用于記錄下載信息,如果是正式項(xiàng)目,需要持久化保存 */ 
  private Map<Long, DownloadInfo> mDownloadMap = new ConcurrentHashMap<Long, DownloadInfo>(); 
  /** 用于記錄觀察者,當(dāng)信息發(fā)送了改變,需要通知他們 */ 
  private List<DownloadObserver> mObservers = new ArrayList<DownloadObserver>(); 
  /** 用于記錄所有下載的任務(wù),方便在取消下載時(shí),通過id能找到該任務(wù)進(jìn)行刪除 */ 
  private Map<Long, DownloadTask> mTaskMap = new ConcurrentHashMap<Long, DownloadTask>(); 
  public static synchronized DownloadManager getInstance() { 
    if (instance == null) { 
      instance = new DownloadManager(); 
    } 
    return instance; 
  } 
  /** 注冊(cè)觀察者 */ 
  public void registerObserver(DownloadObserver observer) { 
    synchronized (mObservers) { 
      if (!mObservers.contains(observer)) { 
        mObservers.add(observer); 
      } 
    } 
  } 
  /** 反注冊(cè)觀察者 */ 
  public void unRegisterObserver(DownloadObserver observer) { 
    synchronized (mObservers) { 
      if (mObservers.contains(observer)) { 
        mObservers.remove(observer); 
      } 
    } 
  } 
  /** 當(dāng)下載狀態(tài)發(fā)送改變的時(shí)候回調(diào) */ 
  public void notifyDownloadStateChanged(DownloadInfo info) { 
    synchronized (mObservers) { 
      for (DownloadObserver observer : mObservers) { 
        observer.onDownloadStateChanged(info); 
      } 
    } 
  } 
  /** 當(dāng)下載進(jìn)度發(fā)送改變的時(shí)候回調(diào) */ 
  public void notifyDownloadProgressed(DownloadInfo info) { 
    synchronized (mObservers) { 
      for (DownloadObserver observer : mObservers) { 
        observer.onDownloadProgressed(info); 
      } 
    } 
  } 
  /** 下載,需要傳入一個(gè)appInfo對(duì)象 */ 
  public synchronized void download(AppInfo appInfo) { 
    // 先判斷是否有這個(gè)app的下載信息 
    DownloadInfo info = mDownloadMap.get(appInfo.getId()); 
    if (info == null) {// 如果沒有,則根據(jù)appInfo創(chuàng)建一個(gè)新的下載信息 
      info = DownloadInfo.clone(appInfo); 
      mDownloadMap.put(appInfo.getId(), info); 
    } 
    // 判斷狀態(tài)是否為STATE_NONE、STATE_PAUSED、STATE_ERROR。只有這3種狀態(tài)才能進(jìn)行下載,其他狀態(tài)不予處理 
    if (info.getDownloadState() == STATE_NONE 
        || info.getDownloadState() == STATE_PAUSED 
        || info.getDownloadState() == STATE_ERROR) { 
      // 下載之前,把狀態(tài)設(shè)置為STATE_WAITING,因?yàn)榇藭r(shí)并沒有產(chǎn)開始下載,只是把任務(wù)放入了線程池中,當(dāng)任務(wù)真正開始執(zhí)行時(shí),才會(huì)改為STATE_DOWNLOADING 
      info.setDownloadState(STATE_WAITING); 
      notifyDownloadStateChanged(info);// 每次狀態(tài)發(fā)生改變,都需要回調(diào)該方法通知所有觀察者 
      DownloadTask task = new DownloadTask(info);// 創(chuàng)建一個(gè)下載任務(wù),放入線程池 
      mTaskMap.put(info.getId(), task); 
      ThreadManager.getDownloadPool().execute(task); 
    } 
  } 
  /** 暫停下載 */ 
  public synchronized void pause(AppInfo appInfo) { 
    stopDownload(appInfo); 
    DownloadInfo info = mDownloadMap.get(appInfo.getId());// 找出下載信息 
    if (info != null) {// 修改下載狀態(tài) 
      info.setDownloadState(STATE_PAUSED); 
      notifyDownloadStateChanged(info); 
    } 
  } 
  /** 取消下載,邏輯和暫停類似,只是需要?jiǎng)h除已下載的文件 */ 
  public synchronized void cancel(AppInfo appInfo) { 
    stopDownload(appInfo); 
    DownloadInfo info = mDownloadMap.get(appInfo.getId());// 找出下載信息 
    if (info != null) {// 修改下載狀態(tài)并刪除文件 
      info.setDownloadState(STATE_NONE); 
      notifyDownloadStateChanged(info); 
      info.setCurrentSize(0); 
      File file = new File(info.getPath()); 
      file.delete(); 
    } 
  } 
  /** 安裝應(yīng)用 */ 
  public synchronized void install(AppInfo appInfo) { 
    stopDownload(appInfo); 
    DownloadInfo info = mDownloadMap.get(appInfo.getId());// 找出下載信息 
    if (info != null) {// 發(fā)送安裝的意圖 
      Intent installIntent = new Intent(Intent.ACTION_VIEW); 
      installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
      installIntent.setDataAndType(Uri.parse("file://" + info.getPath()), 
          "application/vnd.android.package-archive"); 
      AppUtil.getContext().startActivity(installIntent); 
    } 
    notifyDownloadStateChanged(info); 
  } 
  /** 啟動(dòng)應(yīng)用,啟動(dòng)應(yīng)用是最后一個(gè) */ 
  public synchronized void open(AppInfo appInfo) { 
    try { 
      Context context = AppUtil.getContext(); 
      // 獲取啟動(dòng)Intent 
      Intent intent = context.getPackageManager() 
          .getLaunchIntentForPackage(appInfo.getPackageName()); 
      context.startActivity(intent); 
    } catch (Exception e) { 
    } 
  } 
  /** 如果該下載任務(wù)還處于線程池中,且沒有執(zhí)行,先從線程池中移除 */ 
  private void stopDownload(AppInfo appInfo) { 
    DownloadTask task = mTaskMap.remove(appInfo.getId());// 先從集合中找出下載任務(wù) 
    if (task != null) { 
      ThreadManager.getDownloadPool().cancel(task);// 然后從線程池中移除 
    } 
  } 
  /** 獲取下載信息 */ 
  public synchronized DownloadInfo getDownloadInfo(long id) { 
    return mDownloadMap.get(id); 
  } 
  public synchronized void setDownloadInfo(long id, DownloadInfo info) { 
    mDownloadMap.put(id, info); 
  } 
  /** 下載任務(wù) */ 
  public class DownloadTask implements Runnable { 
    private DownloadInfo info; 
    public DownloadTask(DownloadInfo info) { 
      this.info = info; 
    } 
    @Override 
    public void run() { 
      info.setDownloadState(STATE_DOWNLOADING);// 先改變下載狀態(tài) 
      notifyDownloadStateChanged(info); 
      File file = new File(info.getPath());// 獲取下載文件 
      HttpResult httpResult = null; 
      InputStream stream = null; 
      if (info.getCurrentSize() == 0 || !file.exists() 
          || file.length() != info.getCurrentSize()) { 
        // 如果文件不存在,或者進(jìn)度為0,或者進(jìn)度和文件長度不相符,就需要重新下載 
        info.setCurrentSize(0); 
        file.delete(); 
      } 
      httpResult = HttpHelper.download(info.getUrl()); 
      // else { 
      // // //文件存在且長度和進(jìn)度相等,采用斷點(diǎn)下載 
      // httpResult = HttpHelper.download(info.getUrl() + "&range=" + 
      // info.getCurrentSize()); 
      // } 
      if (httpResult == null 
          || (stream = httpResult.getInputStream()) == null) { 
        info.setDownloadState(STATE_ERROR);// 沒有下載內(nèi)容返回,修改為錯(cuò)誤狀態(tài) 
        notifyDownloadStateChanged(info); 
      } else { 
        try { 
          skipBytesFromStream(stream, info.getCurrentSize()); 
        } catch (Exception e1) { 
          e1.printStackTrace(); 
        } 
        FileOutputStream fos = null; 
        try { 
          fos = new FileOutputStream(file, true); 
          int count = -1; 
          byte[] buffer = new byte[1024]; 
          while (((count = stream.read(buffer)) != -1) 
              && info.getDownloadState() == STATE_DOWNLOADING) { 
            // 每次讀取到數(shù)據(jù)后,都需要判斷是否為下載狀態(tài),如果不是,下載需要終止,如果是,則刷新進(jìn)度 
            fos.write(buffer, 0, count); 
            fos.flush(); 
            info.setCurrentSize(info.getCurrentSize() + count); 
            notifyDownloadProgressed(info);// 刷新進(jìn)度 
          } 
        } catch (Exception e) { 
          info.setDownloadState(STATE_ERROR); 
          notifyDownloadStateChanged(info); 
          info.setCurrentSize(0); 
          file.delete(); 
        } finally { 
          IOUtils.close(fos); 
          if (httpResult != null) { 
            httpResult.close(); 
          } 
        } 
        // 判斷進(jìn)度是否和app總長度相等 
        if (info.getCurrentSize() == info.getAppSize()) { 
          info.setDownloadState(STATE_DOWNLOADED); 
          notifyDownloadStateChanged(info); 
        } else if (info.getDownloadState() == STATE_PAUSED) {// 判斷狀態(tài) 
          notifyDownloadStateChanged(info); 
        } else { 
          info.setDownloadState(STATE_ERROR); 
          notifyDownloadStateChanged(info); 
          info.setCurrentSize(0);// 錯(cuò)誤狀態(tài)需要?jiǎng)h除文件 
          file.delete(); 
        } 
      } 
      mTaskMap.remove(info.getId()); 
    } 
  } 
  public interface DownloadObserver { 
    public abstract void onDownloadStateChanged(DownloadInfo info); 
    public abstract void onDownloadProgressed(DownloadInfo info); 
  } 
  /* 重寫了Inpustream 中的skip(long n) 方法,將數(shù)據(jù)流中起始的n 個(gè)字節(jié)跳過 */ 
  private long skipBytesFromStream(InputStream inputStream, long n) { 
    long remaining = n; 
    // SKIP_BUFFER_SIZE is used to determine the size of skipBuffer 
    int SKIP_BUFFER_SIZE = 10000; 
    // skipBuffer is initialized in skip(long), if needed. 
    byte[] skipBuffer = null; 
    int nr = 0; 
    if (skipBuffer == null) { 
      skipBuffer = new byte[SKIP_BUFFER_SIZE]; 
    } 
    byte[] localSkipBuffer = skipBuffer; 
    if (n <= 0) { 
      return 0; 
    } 
    while (remaining > 0) { 
      try { 
        long skip = inputStream.skip(10000); 
        nr = inputStream.read(localSkipBuffer, 0, 
            (int) Math.min(SKIP_BUFFER_SIZE, remaining)); 
      } catch (IOException e) { 
        e.printStackTrace(); 
      } 
      if (nr < 0) { 
        break; 
      } 
      remaining -= nr; 
    } 
    return n - remaining; 
  } 
} 

有兩點(diǎn)需要說明,關(guān)于點(diǎn)擊暫停后,再繼續(xù)下載有兩種方式可以實(shí)現(xiàn)
第一種 點(diǎn)擊暫停的時(shí)候 記錄下載了 多少,然后 再點(diǎn)擊 繼續(xù)下載 時(shí),告訴服務(wù)器, 讓服務(wù)器接著 上次的數(shù)據(jù) 往本地傳遞,
代碼是我們 DownloadTask 下載時(shí)候,判斷一下

// //文件存在且長度和進(jìn)度相等,采用斷點(diǎn)下載 
      httpResult = HttpHelper.download(info.getUrl() + "&range=" + info.getCurrentSize()); 

通過 range 來區(qū)分 當(dāng)前的下載size.
服務(wù)器 處理的代碼 也很簡單 就是一句話
String range = req.getParameter(“range”); 拿到 range 判斷 range 存在不存在。
如果不存在

FileInputStream stream = new FileInputStream(file); 
     int count = -1; 
     byte[] buffer = new byte[1024]; 
     while ((count = stream.read(buffer)) != -1) { 
       SystemClock.sleep(20); 
       out.write(buffer, 0, count); 
       out.flush(); 
     } 
     stream.close(); 
     out.close(); 

如果存在那么跳過range 個(gè)字節(jié)


RandomAccessFile raf = new RandomAccessFile(file, "r"); 
      raf.seek(Long.valueOf(range));  
      int count = -1; 
      byte[] buffer = new byte[1024]; 
      while ((count = raf.read(buffer)) != -1) { 
        SystemClock.sleep(10); 
        out.write(buffer, 0, count); 
        out.flush(); 
      } 
      raf.close(); 
      out.close(); 

另一種方式是本地處理,這個(gè)demo 中就是本地處理的, 但是有一個(gè)問題, 因?yàn)?Java api的原因 ,inputStream.skip() 方法 并不能準(zhǔn)確的 跳過多少個(gè)字節(jié),
而是 小于你想要跳過的字節(jié),所以 你要去遍歷 一直到 滿足你要跳過的字節(jié) 在繼續(xù)寫, 因?yàn)?這樣的方法有一個(gè)缺點(diǎn),就是在下載很大的文件,
比如文件大小20M ,當(dāng)已經(jīng)下載了15M 此時(shí)你去暫停,在繼續(xù)下載,那么要跳過前面的15M 將會(huì)話費(fèi)很多時(shí)間。

此實(shí)現(xiàn)方式還有很多缺陷,所以在實(shí)際中要下載大的文件,還是不能用。

--------------------------------------------------------------------- 改進(jìn)版-------------------------------------------------------------------------------

先來介紹下這次改進(jìn)的兩點(diǎn):

 第一點(diǎn) ,前面說過 項(xiàng)目 只適合學(xué)習(xí),作為商用的話, 效率不高,是因?yàn)楫?dāng)時(shí)點(diǎn)擊暫停 ,在點(diǎn)擊下載繼續(xù)下載時(shí)候,如果文件前面下載部分較大,會(huì)比較慢,因?yàn)閖ava 的 inputstream的 skip(longsize) 跳過字節(jié) 這個(gè)方法 并不能按照你 想要跳過的字節(jié),而是跳過的往往是比較小的,所以要不斷遍歷,直到返回滿足條件 ,比較耗時(shí)。打個(gè)比方,文件大小30M ,你下載了20M,你點(diǎn)了暫停然后繼續(xù)點(diǎn)下載,就要跳過這20M,但是你用skip 方法 可能每次跳過4096 字節(jié),這樣要跳過20M的時(shí)間 就會(huì)很長。這樣應(yīng)該好理解。
第二點(diǎn),原來 項(xiàng)目中,你這一次下載沒有完成,下次在下載是刪除掉原來的從新 下載,這次改成繼續(xù)上次的地方接著下載。
吐槽下,關(guān)于下載,我最近一周 一直在看 開源的download, 但是 無奈水平有限,收獲甚微,往往是看到最后 腦袋短路。大哭
這次改的方式比較簡單,只改動(dòng)了 項(xiàng)目中 DownloadManager 這個(gè)類。在來看下 DownloadManager這個(gè)類 的run 方法,
   

@Override 
    public void run() { 
      info.setDownloadState(STATE_DOWNLOADING);// 先改變下載狀態(tài) 
      notifyDownloadStateChanged(info); 
      File file = new File(info.getPath());// 獲取下載文件 
      HttpResult httpResult = null; 
      InputStream stream = null; 
      if (info.getCurrentSize() == 0 || !file.exists() 
          || file.length() != info.getCurrentSize()) { 
        // 如果文件不存在,或者進(jìn)度為0,或者進(jìn)度和文件長度不相符,就需要重新下載 
<span>        </span>info.setCurrentSize(0); 
        file.delete(); 
      } 
      httpResult = HttpHelper.download(info.getUrl()); 
      if (httpResult == null 
          || (stream = httpResult.getInputStream()) == null) { 
        info.setDownloadState(STATE_ERROR);// 沒有下載內(nèi)容返回,修改為錯(cuò)誤狀態(tài) 
        notifyDownloadStateChanged(info); 
      } else { 
        try { 
          skipBytesFromStream(stream, info.getCurrentSize()); 
        } catch (Exception e1) { 
          e1.printStackTrace(); 
        } 
        FileOutputStream fos = null; 
        try { 
          fos = new FileOutputStream(file, true); 
          int count = -1; 
          byte[] buffer = new byte[1024]; 
          while (((count = stream.read(buffer)) != -1) 
              && info.getDownloadState() == STATE_DOWNLOADING) { 
            // 每次讀取到數(shù)據(jù)后,都需要判斷是否為下載狀態(tài),如果不是,下載需要終止,如果是,則刷新進(jìn)度 
            fos.write(buffer, 0, count); 
            fos.flush(); 
            info.setCurrentSize(info.getCurrentSize() + count); 
            notifyDownloadProgressed(info);// 刷新進(jìn)度 
          } 
        } catch (Exception e) { 
          info.setDownloadState(STATE_ERROR); 
          notifyDownloadStateChanged(info); 
          info.setCurrentSize(0); 
          file.delete(); 
        } finally { 
          IOUtils.close(fos); 
          if (httpResult != null) { 
            httpResult.close(); 
          } 
        } 
<span>        </span>// 判斷進(jìn)度是否和app總長度相等 
        if (info.getCurrentSize() == info.getAppSize()) { 
          info.setDownloadState(STATE_DOWNLOADED); 
          notifyDownloadStateChanged(info); 
        } else if (info.getDownloadState() == STATE_PAUSED) {// 判斷狀態(tài) 
          notifyDownloadStateChanged(info); 
        } else { 
          info.setDownloadState(STATE_ERROR); 
          notifyDownloadStateChanged(info); 
          info.setCurrentSize(0);// 錯(cuò)誤狀態(tài)需要?jiǎng)h除文件 
          file.delete(); 
        } 
      } 
      mTaskMap.remove(info.getId()); 
    } 

從服務(wù)器 返回的數(shù)據(jù)流  stream  最終是在 HttpHelper 這個(gè)類中 

HttpResponse response = httpClient.execute(requestBase, httpContext);//訪問網(wǎng)絡(luò) 

通過  httpclient 去聯(lián)網(wǎng)請(qǐng)求的  。
我沒有試過 httpclient    addHeader("Range", "bytes=" + begin + "-" + end); 可不可以進(jìn)行繼續(xù)下載。
而是改成了 通過 httpurlconnection 去請(qǐng)求數(shù)據(jù)
現(xiàn)在  的run()方法是這樣的。
   

@Override 
    public void run() { 
      info.setDownloadState(STATE_DOWNLOADING);// 先改變下載狀態(tài) 
      notifyDownloadStateChanged(info); 
      File file = new File(info.getPath());// 獲取下載文件 
      /**********************************************************/ 
//     try { 
        try { 
          URL url = new URL(info.getUrl()); 
          HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
          conn.setRequestMethod("GET"); 
          conn.setConnectTimeout(30000); 
          conn.setReadTimeout(30000); 
          if (!file.exists()) { 
            info.setCurrentSize(0); 
            file.delete(); 
          } else if (file.length() > info.getAppSize()) { 
            info.setCurrentSize(0); 
            file.delete(); 
          } else if (file.length() == info.getAppSize()) { 
          } else if (file.length() < info.getAppSize()) { 
            info.setCurrentSize(file.length()); 
          } 
          if (info.getCurrentSize() == 0 || !file.exists() || file.length() != info.getCurrentSize()) { 
            // 如果文件不存在,或者進(jìn)度為0,或者進(jìn)度和文件長度不相符,就需要重新下載 
            info.setCurrentSize(0); 
            file.delete(); 
          } else if (file.length() == info.getCurrentSize() && file.length() < info.getAppSize()) { 
            conn.setRequestProperty("Range", "bytes=" + info.getCurrentSize() + "-" + info.getAppSize()); 
          } 
          int code = conn.getResponseCode(); 
          RandomAccessFile raf = new RandomAccessFile(file, "rw"); 
          InputStream is = conn.getInputStream(); 
          byte[] buffer = new byte[1024 * 8]; 
          int len = -1; 
          int total = 0;// 當(dāng)前線程下載的總的數(shù)據(jù)的長度 
          if (code == 200) { 
          } else if (code == 206) { 
            raf.seek(file.length()); 
          } 
          while (((len = is.read(buffer)) != -1) && (info.getDownloadState() == STATE_DOWNLOADING)) { // 下載數(shù)據(jù)的過程。 
            raf.write(buffer, 0, len); 
            total += len;// 需要記錄當(dāng)前的數(shù)據(jù)。 
            info.setCurrentSize(info.getCurrentSize() + len); 
            notifyDownloadProgressed(info);// 刷新進(jìn)度 
          } 
          is.close(); 
          raf.close(); 
        } catch (MalformedURLException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } catch (ProtocolException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } catch (FileNotFoundException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } catch (IOException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } 
        /*************************對(duì)于各種情況,需要?jiǎng)h除下載任務(wù),從新下載的 請(qǐng)自己改動(dòng)代碼*****************************/ 
        // 判斷進(jìn)度是否和app總長度相等 
//     } catch (Exception e) { 
//       System.out.println(e.toString()); 
//       info.setDownloadState(STATE_ERROR); 
//       info.setCurrentSize(0); 
//       file.delete(); 
//       e.printStackTrace(); 
//     } 
      if (info.getCurrentSize() == info.getAppSize()) { 
        info.setDownloadState(STATE_DOWNLOADED); 
        notifyDownloadStateChanged(info); 
      } else if (info.getDownloadState() == STATE_PAUSED) {// 判斷狀態(tài) 
        notifyDownloadStateChanged(info); 
      } else { 
        info.setDownloadState(STATE_ERROR); 
        notifyDownloadStateChanged(info); 
        info.setCurrentSize(0);// 錯(cuò)誤狀態(tài)需要?jiǎng)h除文件 
        file.delete(); 
      } 
      /**********************************************************/ 
      mTaskMap.remove(info.getId()); 
    } 

先判斷文件存不存在,以及大小是否滿足條件, 在這里做判斷

if (info.getCurrentSize() == 0 || !file.exists() || file.length() != info.getCurrentSize()) { 
  // 如果文件不存在,或者進(jìn)度為0,或者進(jìn)度和文件長度不相符,就需要重新下載 
  info.setCurrentSize(0); 
  file.delete(); 
  } else if (file.length() == info.getCurrentSize() && file.length() < info.getAppSize()) { 
     conn.setRequestProperty("Range", "bytes=" + info.getCurrentSize() + "-" + info.getAppSize()); 
   } 

如果 文件當(dāng)前大小為0,或者文件不存在,或者長度不等于當(dāng)前長度,則重新下載,否則 設(shè)置 Range
下面 判斷 code  正常情況下code =200 表示成功,如果 設(shè)置了Range  那么 code 返回 206 表示正常。這個(gè)時(shí)候我們通過RandomAccessFile
RandomAccessFile  這個(gè) 類實(shí)現(xiàn)了 RandomAccessFile implements DataInput, DataOutput,就是一個(gè)既可以讀也可以寫的類。
RandomAccessFile 這個(gè)類來 處理 跳過多少字節(jié), 前面 我說過 inpuStream.skeep() 方法 不準(zhǔn)確,但是  RandomAccessFile  這個(gè)類是可以的。


RandomAccessFile raf = new RandomAccessFile(file, "rw"); 
InputStream is = conn.getInputStream(); 
byte[] buffer = new byte[1024 * 8]; 
int len = -1; 
int total = 0;// 當(dāng)前線程下載的總的數(shù)據(jù)的長度 
if (code == 200) { 
} else if (code == 206) { 
raf.seek(file.length()); 
} 

通過 seek 方法 跳過 這些字節(jié)。
然后

while (((len = is.read(buffer)) != -1) && (info.getDownloadState() == STATE_DOWNLOADING)) { // 下載數(shù)據(jù)的過程。 
raf.write(buffer, 0, len); 
total += len;// 需要記錄當(dāng)前的數(shù)據(jù)。 
info.setCurrentSize(info.getCurrentSize() + len); 
notifyDownloadProgressed(info);// 刷新進(jìn)度 
} 
is.close(); 
raf.close(); 

很普通的代碼,把數(shù)據(jù)寫出去。不斷刷新當(dāng)前進(jìn)度, 最后關(guān)閉流。
這樣就可以保證快速的暫停繼續(xù)下載,并且 本次下載 沒有完成,點(diǎn)了暫停,下次進(jìn)應(yīng)用,繼續(xù)下載的時(shí)候 會(huì)接著上一次下載,但是斷網(wǎng),或者你自己把網(wǎng)關(guān)掉 ,下次在恢復(fù)網(wǎng)絡(luò),或者 在點(diǎn)下載,我并沒有處理,有需要的就自己處理下吧,應(yīng)該是捕獲異常 seckouttimeException,然后保存數(shù)據(jù)。自己動(dòng)手試下就知道了。

本次就到這里,希望對(duì)大家學(xué)習(xí)Android版多線程下載 仿下載助手(最新)有所啟發(fā)。

相關(guān)文章

最新評(píng)論