Android開(kāi)發(fā)(7) 文件下載

概述

我們?cè)陂_(kāi)發(fā)中經(jīng)常需要從服務(wù)器下載文件,下載的內(nèi)容可能有交換的信息,緩存的圖片,程序更新包等。我們使用URLConnection來(lái)實(shí)現(xiàn)下載。

先看幾行代碼:

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

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

如上面的代碼所示,指定一個(gè)下載的目標(biāo)鏈接,我們上面指定了一個(gè)圖片地址。然后構(gòu)建一個(gè)URL對(duì)象,調(diào)用該對(duì)象的openConnection方法來(lái)建立一個(gè)數(shù)據(jù)通路(連接)。代碼的最后一行使用 con.getInputStream,拿到一個(gè)輸入流對(duì)象,通過(guò)這個(gè)流對(duì)象我們就可以讀取到這個(gè)文件的內(nèi)容了。下面要做的,就是讀取這個(gè)流,將流寫入我們的本地文件。

不過(guò)在這之前,我們還要說(shuō)下這個(gè)方法:

  //獲得文件的長(zhǎng)度
            int contentLength = con.getContentLength();
            System.out.println("長(zhǎng)度 :"+contentLength);

獲得文件長(zhǎng)度的方法

ContentLength是不很熟啊。它是http協(xié)議里描述頭(head)部分的描述屬性之一。實(shí)際這里是發(fā)送了一個(gè)http請(qǐng)求,分析了返回(response)里數(shù)據(jù)內(nèi)容。

我們常常會(huì)把文件下載到手機(jī)的存儲(chǔ)卡里,所以還會(huì)用到獲得存儲(chǔ)卡路徑的方法:

獲得存儲(chǔ)卡路徑,構(gòu)成 保存文件的目標(biāo)路徑

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

Environment.getExternalStorageDirectory() 方法會(huì)返回一個(gè)字符串,指示了存儲(chǔ)卡的路徑。我們拼接字符串出一個(gè)準(zhǔn)備存放下載文件的文件夾。并先判斷文件夾是是否存在,如果不存在,則新建一個(gè)文件夾。

做完了上面的準(zhǔn)備后,基本就能實(shí)現(xiàn)下載了。

完整代碼

下載前的準(zhǔn)備工作:

//要下載的文件路徑
String urlDownload = "";
//urlDownload =  "http://192.168.3.39/text.txt%22;
urlDownload =     "http://www.baidu.com/img/baidu_sylogo1.gif%22;
// 獲得存儲(chǔ)卡路徑,構(gòu)成 保存文件的目標(biāo)路徑
String dirName = "";
dirName = Environment.getExternalStorageDirectory()+"/MyDownload/";
File f = new File(dirName);
if(!f.exists())
{
    f.mkdir();
}

下載的操作

//準(zhǔn)備拼接新的文件名(保存在存儲(chǔ)卡后的文件名)
      String newFilename = _urlStr.substring(_urlStr.lastIndexOf("/")+1);
      newFilename = _dirName + newFilename;
    File file = new File(newFilename);
    //如果目標(biāo)文件已經(jīng)存在,則刪除。產(chǎn)生覆蓋舊文件的效果
    if(file.exists())
    {
        file.delete();
    }
    try {
           // 構(gòu)造URL    
            URL url = new URL(_urlStr);    
            // 打開(kāi)連接    
            URLConnection con = url.openConnection(); 
            //獲得文件的長(zhǎng)度
            int contentLength = con.getContentLength();
            System.out.println("長(zhǎng)度 :"+contentLength);
            // 輸入流    
            InputStream is = con.getInputStream();   
          
            // 1K的數(shù)據(jù)緩沖    
            byte[] bs = new byte[1024];    
            // 讀取到的數(shù)據(jù)長(zhǎng)度    
            int len;    
           // 輸出的文件流    
            OutputStream os = new FileOutputStream(newFilename);    
            // 開(kāi)始讀取    
            while ((len = is.read(bs)) != -1) {    
              os.write(bs, 0, len);    
            }   
            // 完畢,關(guān)閉所有鏈接    
            os.close();   
            is.close(); 
        
    } catch (Exception e) {
            e.printStackTrace();
    }

完整的代碼下載

進(jìn)階篇 - 增加進(jìn)度條提示下載進(jìn)度

我們先來(lái)看下進(jìn)度條

<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"/>

上面展示了一個(gè)水平的進(jìn)度條。max屬性指定了最大值,progress指示當(dāng)前的進(jìn)度位置。style指定了一個(gè)現(xiàn)實(shí)風(fēng)格。

“觀察者”模式

不得不說(shuō)的一個(gè)是設(shè)計(jì)模式里的“觀察者”模式。觀察者模式提供了一個(gè)“讓一個(gè)觀察者去觀察一個(gè)對(duì)象,當(dāng)觀察的目標(biāo)發(fā)生變化時(shí),通知給 訂閱了觀察結(jié)果的對(duì)象 ”。這句話純屬于個(gè)人理解。

它表達(dá)了幾個(gè)對(duì)象:
1.訂閱了“觀察結(jié)果的對(duì)象”,該對(duì)象會(huì)收到“觀察者”的通知。
2.觀察者對(duì)象,一個(gè)緊盯這 目標(biāo)對(duì)象 的對(duì)象。它負(fù)責(zé)將 觀察的目標(biāo) 的變化 通知給 “訂閱者”
3.被觀察的目標(biāo),會(huì)發(fā)生變化的目標(biāo)對(duì)象,它的變化及時(shí)的都被觀察者所知。

在我們的下載時(shí)我們的幾個(gè)對(duì)象是

1.進(jìn)度條,是訂閱者,它接受觀察者對(duì)象的消息,來(lái)顯示自己的進(jìn)度條位置。

2.觀察者,是一個(gè)handler對(duì)象。該對(duì)象適合在線程間傳遞消息。我們就用它傳遞消息的特點(diǎn),并且該對(duì)象屬于android平臺(tái)核心框架,和主界面的消息循環(huán)有聯(lián)系。

3.被觀察的目標(biāo)就是下載的過(guò)程了。這個(gè)過(guò)程中下載文件的進(jìn)度。

我們分別實(shí)現(xiàn)它

private Handler myHandler = new Handler(){
 public void handleMessage(android.os.Message msg) {
  //獲得進(jìn)度,該進(jìn)度由實(shí)際的操作進(jìn)行通知。這里負(fù)責(zé)對(duì)通知進(jìn)行處理
  int progress = msg.arg1;
  //設(shè)置進(jìn)度條的當(dāng)前位置
  _ProgressBar01.setProgress(progress);
  if(progress == 100)
  {
   Toast.makeText(getApplicationContext(), "下載成功", 0).show();
   
  }
  
 };
};

我們構(gòu)建一個(gè)myHandler 對(duì)象,并重載了它的handleMessage方法,該方法會(huì)捕獲(收到)所有發(fā)送給myHandler 的消息。我們接收消息,并根據(jù)消息攜帶的信息arg1作為當(dāng)前的進(jìn)度表示。

下面我們將這個(gè)myHandler 傳遞給下載的線程

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

如上面這行代碼所示,F(xiàn)ileDownloader對(duì)象是個(gè)下載器對(duì)象,它負(fù)責(zé)下載文件,同時(shí)他和觀察者myHandler關(guān)聯(lián)。有了消息,它就告訴這個(gè)觀察者。觀察者收到消息,通知給訂閱了消息的對(duì)象(本文為進(jìn)度條)。

下面看下如何進(jìn)行下載進(jìn)程的:

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

我們記錄我們當(dāng)前從服務(wù)器讀了多少字節(jié),又知道總共需要下載多少字節(jié)。計(jì)算后,得到一個(gè)完成了多少的百分比。將這個(gè)百分比通知給 觀察者。

完整的代碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容