天天看點

Android開發(7) 檔案下載下傳概述完整代碼進階篇 - 增加進度條提示下載下傳進度

概述

我們在開發中經常需要從伺服器下載下傳檔案,下載下傳的内容可能有交換的資訊,緩存的圖檔,程式更新包等。我們使用URLConnection來實作下載下傳。

先看幾行代碼:

String urlDownload = "";
           urlDownload = "http://www.baidu.com/img/baidu_sylogo1.gif";

            URL url = new URL(urlDownload );    
            // 打開連接配接    
            URLConnection con = url.openConnection(); 
       
            // 輸入流    
            InputStream is = con.getInputStream();             

複制

如上面的代碼所示,指定一個下載下傳的目标連結,我們上面指定了一個圖檔位址。然後建構一個URL對象,調用該對象的openConnection方法來建立一個資料通路(連接配接)。代碼的最後一行使用 con.getInputStream,拿到一個輸入流對象,通過這個流對象我們就可以讀取到這個檔案的内容了。下面要做的,就是讀取這個流,将流寫入我們的本地檔案。

不過在這之前,我們還要說下這個方法:

//獲得檔案的長度
            int contentLength = con.getContentLength();
            System.out.println("長度 :"+contentLength);           

複制

獲得檔案長度的方法

ContentLength是不很熟啊。它是http協定裡描述頭(head)部分的描述屬性之一。實際這裡是發送了一個http請求,分析了傳回(response)裡資料内容。

我們常常會把檔案下載下傳到手機的存儲卡裡,是以還會用到獲得存儲卡路徑的方法:

獲得存儲卡路徑,構成 儲存檔案的目标路徑

String dirName = "";
            dirName = Environment.getExternalStorageDirectory()+"/MyDownload/";
            File f = new File(dirName);
            if(!f.exists())
            {
                f.mkdir();
            }           

複制

Environment.getExternalStorageDirectory() 方法會傳回一個字元串,訓示了存儲卡的路徑。我們拼接字元串出一個準備存放下載下傳檔案的檔案夾。并先判斷檔案夾是是否存在,如果不存在,則建立一個檔案夾。

做完了上面的準備後,基本就能實作下載下傳了。

完整代碼

下載下傳前的準備工作:

//要下載下傳的檔案路徑
String urlDownload = "";
//urlDownload =  "http://192.168.3.39/text.txt%22;
urlDownload =     "http://www.baidu.com/img/baidu_sylogo1.gif%22;
// 獲得存儲卡路徑,構成 儲存檔案的目标路徑
String dirName = "";
dirName = Environment.getExternalStorageDirectory()+"/MyDownload/";
File f = new File(dirName);
if(!f.exists())
{
    f.mkdir();
}           

複制

下載下傳的操作

//準備拼接新的檔案名(儲存在存儲卡後的檔案名)
      String newFilename = _urlStr.substring(_urlStr.lastIndexOf("/")+1);
      newFilename = _dirName + newFilename;
    File file = new File(newFilename);
    //如果目标檔案已經存在,則删除。産生覆寫舊檔案的效果
    if(file.exists())
    {
        file.delete();
    }
    try {
           // 構造URL    
            URL url = new URL(_urlStr);    
            // 打開連接配接    
            URLConnection con = url.openConnection(); 
            //獲得檔案的長度
            int contentLength = con.getContentLength();
            System.out.println("長度 :"+contentLength);
            // 輸入流    
            InputStream is = con.getInputStream();   
          
            // 1K的資料緩沖    
            byte[] bs = new byte[1024];    
            // 讀取到的資料長度    
            int len;    
           // 輸出的檔案流    
            OutputStream os = new FileOutputStream(newFilename);    
            // 開始讀取    
            while ((len = is.read(bs)) != -1) {    
              os.write(bs, 0, len);    
            }   
            // 完畢,關閉所有連結    
            os.close();   
            is.close(); 
        
    } catch (Exception e) {
            e.printStackTrace();
    }           

複制

完整的代碼下載下傳

進階篇 - 增加進度條提示下載下傳進度

我們先來看下進度條

<ProgressBar 
  android:id="@+id/ProgressBar01" 
  style="?android:attr/progressBarStyleHorizontal" 
  android:layout_height="wrap_content" 
  android:visibility="visible" 
  android:max="100"
  android:progress="1"
  android:layout_width="200dp"/>           

複制

上面展示了一個水準的進度條。max屬性指定了最大值,progress訓示目前的進度位置。style指定了一個現實風格。

“觀察者”模式

不得不說的一個是設計模式裡的“觀察者”模式。觀察者模式提供了一個“讓一個觀察者去觀察一個對象,當觀察的目标發生變化時,通知給 訂閱了觀察結果的對象 ”。這句話純屬于個人了解。

它表達了幾個對象:

1.訂閱了“觀察結果的對象”,該對象會收到“觀察者”的通知。

2.觀察者對象,一個緊盯這 目标對象 的對象。它負責将 觀察的目标 的變化 通知給 “訂閱者”

3.被觀察的目标,會發生變化的目标對象,它的變化及時的都被觀察者所知。

在我們的下載下傳時我們的幾個對象是

1.進度條,是訂閱者,它接受觀察者對象的消息,來顯示自己的進度條位置。

2.觀察者,是一個handler對象。該對象适合線上程間傳遞消息。我們就用它傳遞消息的特點,并且該對象屬于android平台核心架構,和主界面的消息循環有聯系。

3.被觀察的目标就是下載下傳的過程了。這個過程中下載下傳檔案的進度。

我們分别實作它

private Handler myHandler = new Handler(){
 public void handleMessage(android.os.Message msg) {
  //獲得進度,該進度由實際的操作進行通知。這裡負責對通知進行處理
  int progress = msg.arg1;
  //設定進度條的目前位置
  _ProgressBar01.setProgress(progress);
  if(progress == 100)
  {
   Toast.makeText(getApplicationContext(), "下載下傳成功", 0).show();
   
  }
  
 };
};           

複制

我們建構一個myHandler 對象,并重載了它的handleMessage方法,該方法會捕獲(收到)所有發送給myHandler 的消息。我們接收消息,并根據消息攜帶的資訊arg1作為目前的進度表示。

下面我們将這個myHandler 傳遞給下載下傳的線程

new FileDownloader(urlDownload,dirName,myHandler).start();           

複制

如上面這行代碼所示,FileDownloader對象是個下載下傳器對象,它負責下載下傳檔案,同時他和觀察者myHandler關聯。有了消息,它就告訴這個觀察者。觀察者收到消息,通知給訂閱了消息的對象(本文為進度條)。

下面看下如何進行下載下傳程序的:

// 構造URL    
   URL url = new URL(_urlStr);    
   // 打開連接配接    
   URLConnection con = url.openConnection(); 
   //獲得檔案的長度
   int contentLength = con.getContentLength();
   System.out.println("長度 :"+contentLength);
   // 輸入流    
   InputStream is = con.getInputStream();   
 
   int hasRead = 0;//已經讀取了多少
   int progress = 0;
   
   // 1K的資料緩沖    
   byte[] bs = new byte[128];    
   // 讀取到的資料長度    
   int len;    
  // 輸出的檔案流    
   OutputStream os = new FileOutputStream(newFilename);    
   // 開始讀取    
   while ((len = is.read(bs)) != -1) {    
     os.write(bs, 0, len); 
     
     //記錄完成了的多少
     hasRead +=len;
     progress = (int)((double)hasRead/(double)contentLength * 100);//完成的百分比
     //發送通知
     Message msg = _myHandler.obtainMessage();
     msg.arg1 = progress;
     msg.sendToTarget();
     
     Thread.sleep(500);//故意延遲,不然進度條跑的太快看不清楚
   }   
   // 完畢,關閉所有連結    
   os.close();   
   is.close();           

複制

我們記錄我們目前從伺服器讀了多少位元組,又知道總共需要下載下傳多少位元組。計算後,得到一個完成了多少的百分比。将這個百分比通知給 觀察者。

完整的代碼下載下傳