[TEAP早期試讀]Android開發實現後臺下載

陳鋼發表於2012-03-13

圖靈社群按

TEAP是什麼?TEAP是Turingbook Early Access Program的簡稱,即早期試讀,它公佈的是圖靈在途新書未經編輯的內容。一本書的翻譯週期約為3到6個月,如果在翻譯過程中,譯者就能與讀者進行溝通和交流,對整本書的翻譯品質是有幫助的。通過TEAP,讀者可以提前閱讀將來才能出版的內容,譯者也能收穫寶貴的反饋意見,改進翻譯,提高質量。

本書原名為Android Recipes: A Problem-Solution Approach,中文暫定名為《Android攻略》,本文節選自原書第3章第5節。

問題

應用要將一個很大的資源下載到裝置上,例如電影,但在這個過程中無需使用者時刻保持應用處於活躍狀態。

解決方案

(API Level 9)

使用DownloadManager API。SDK在API Level 9中加入了DownloadManager服務,可以將長時間的下載任務交給系統,完全由系統管理。使用這個服務的主要好處就是,在遇到下載失敗、連線改變甚至是裝置重啟等情況時,DownloadManager會自動的繼續下載指定的資源。

原理

程式碼清單3-12中的示例Activity用DownloadManager下載大型圖片檔案。在下載完成時,圖片就會顯示在ImageView中。在使用DownloadManager訪問網際網路內容時,一定要在應用的manifest檔案中宣告android.permission.INTERNET許可權。

程式碼清單3-12 DownloadManager示例Activity

public class DownloadActivity extends Activity { 

private static final String DL_ID = "downloadId"; 
private SharedPreferences prefs; 

private DownloadManager dm; 
private ImageView imageView; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    imageView = new ImageView(this); 
    setContentView(imageView); 

    prefs = PreferenceManager.getDefaultSharedPreferences(this); 
    dm = (DownloadManager)getSystemService(DOWNLOAD_SERVICE); 
} 

@Override 
public void onResume() { 
    super.onResume(); 

    if(!prefs.contains(DL_ID)) { 
        //開始下載 
        Uri resource = Uri.parse("http://www.bigfoto.com/dog-animal.jpg"); 
        DownloadManager.Request request = new DownloadManager.Request(resource); 
        request.setAllowedNetworkTypes(Request.NETWORK_MOBILE |  
            Request.NETWORK_WIFI); 
        request.setAllowedOverRoaming(false); 
        //在通知欄中顯示 
        request.setTitle("Download Sample"); 
        long id = dm.enqueue(request); 
        //儲存id 
        prefs.edit().putLong(DL_ID, id).commit(); 
    } else { 
        //下載已經開始,檢查狀態
        queryDownloadStatus(); 
    } 

    registerReceiver(receiver, 
        new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 
} 

@Override 
public void onPause() { 
    super.onPause(); 
    unregisterReceiver(receiver); 
} 

private BroadcastReceiver receiver = new BroadcastReceiver() { 
    @Override 
    public void onReceive(Context context, Intent intent) { 
        queryDownloadStatus(); 
    } 
}; 

private void queryDownloadStatus() { 
    DownloadManager.Query query = new DownloadManager.Query(); 
    query.setFilterById(prefs.getLong(DL_ID, 0)); 
    Cursor c = dm.query(query); 
    if(c.moveToFirst()) { 
        int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); 
        switch(status) { 
        case DownloadManager.STATUS_PAUSED: 
        case DownloadManager.STATUS_PENDING: 
        case DownloadManager.STATUS_RUNNING: 
            //正在下載,不做任何事情
            break; 
        case DownloadManager.STATUS_SUCCESSFUL: 
            //完成,顯示圖片
            try { 
                ParcelFileDescriptor file =  
                    dm.openDownloadedFile(prefs.getLong(DL_ID, 0)); 
                FileInputStream fis =  
                    new ParcelFileDescriptor.AutoCloseInputStream(file); 
                imageView.setImageBitmap(BitmapFactory.decodeStream(fis)); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
            break; 
        case DownloadManager.STATUS_FAILED: 
            //清除已下載的內容,重新下載
            dm.remove(prefs.getLong(DL_ID, 0)); 
            prefs.edit().clear().commit(); 
            break; 
        } 
    } 
} 

}

重要:在本書出版時,SDK中有一個BUG:在使用DownloadManager時會丟擲一個要求宣告android.permission.ACCESS_ALL_DOWNLOADS的異常。但實際上,只有在manifest中沒有申明android.permission.INTERNET許可權時才會出現這個異常。

在這個例子中,所有的實際工作都是在Activity.onResume()方法中完成的。因此,每次使用者返回該Activity時,應用都會判斷下載的狀態。管理器中的下載任務可以通過呼叫DownloadManager.enqeue()時返回的長長的ID值來引用。在這個例子中,我們將這個值儲存到應用的設定中,這樣就隨時都能監控和獲取下載的內容。

在第一次啟動這個示例應用時,會建立一個表示要下載的內容的DownloadManager.Request物件。至少需要指定遠端資源的Uri來建立請求。不過,還有很多有用的屬性可以用來控制它的行為。這些有用的屬性包括: - Request.setAllowedNetworkTypes() 指定下載所使用的網路型別。 - Request.setAllowedOverRoaming() 設定當裝置處於漫遊模式時是否要下載。 - Request.setTitle() 設定下載在系統通知欄中顯示的標題。 - Request.setDescription() 設定下載在系統通知欄中顯示的描述。

在獲得ID後,應用就用這個ID檢查下載的狀態。註冊一個BroadcastReceiver監聽ACTION_DOWNLOAD_COMPLETE廣播,在下載完成時,應用就可以將下載的圖片檔案設定到Activity的ImageView上。如果在下載完成時Activity處於暫停狀態,那麼就會在下次恢復Activity時檢查下載狀態,並設定ImageView的內容。

要注意ACTION_DOWNLOAD_COMPLETE是DownloadManager對其管理的所有下載任務發出的廣播。正因為如此,我們必須要根據下載ID才能判斷我們的下載是否完成。

相關文章