Android 開發學習程式0.32 dwonloadmanager使用

百密一疏發表於2021-07-15

downloadmanager時Android系統下載器,使用系統下載器可以避免用stream流讀入記憶體可能導致的記憶體溢位問題。以下為downloadmanager初始化部分。apkurl為下載網路路徑。Environment.DIRECTORY_DOWNLOADS 為系統的下載路徑。即下載至外部儲存。

   mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        String apkUrl = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
        Uri resource = Uri.parse(apkUrl);
        DownloadManager.Request request = new DownloadManager.Request(resource);
        //下載的本地路徑,表示設定下載地址為SD卡的Download資料夾,檔名為mobileqq_android.apk。
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "mobileqq_android.apk");
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setVisibleInDownloadsUi(true);
        request.setDescription("xiazaizhong");
        //end 一些非必要的設定
        id = mDownloadManager.enqueue(request);
        //下載後的本地uri
        uri = mDownloadManager.getUriForDownloadedFile(id);

enqueue方法為新增到下載佇列,同時返回的id用於contentobserver監聽下載進度.下載進度監聽程式碼如下:

private DownloadContentObserver observer = new DownloadContentObserver();
    class DownloadContentObserver extends ContentObserver {
        public DownloadContentObserver() {
            super(handler);
        }
        @Override
        public void onChange(boolean selfChange) {
//            updateView();
            if (scheduledExecutorService != null) {
                scheduledExecutorService.scheduleWithFixedDelay(runnable, 0, 3, TimeUnit.SECONDS);
            }
        }
    }
public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
 public void updateView() {
        int[] bytesAndStatus = getBytesAndStatus(id);
        int currentSize = bytesAndStatus[0];//當前大小
        int totalSize = bytesAndStatus[1];//總大小
        int status = bytesAndStatus[2];//下載狀態  1開始 2下載中 8下載完成
        Message.obtain(handler, 0, currentSize, totalSize, status).sendToTarget();
    }
    public int[] getBytesAndStatus(long downloadId) {
        int[] bytesAndStatus = new int[]{-1, -1, 0};
        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = null;
        try {
            c = mDownloadManager.query(query);
            if (c != null && c.moveToFirst()) {
                bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
            }
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return bytesAndStatus;
    }

設定 DownloadContentObserver類監聽下載進度,可在其重寫的 onchange方法中更新UI,即updataView方法,但此會返回大量資料,在下載量大時可使用ScheduledExecutorService 設定定時任務,註冊定時任務設定間隔時間查詢進度,downloadcontentobserver需要在onresume 和 ondestroy方法中註冊和登出程式碼在下,getBytesAndStatus方法獲取佇列中陣列當前下載的進度 包括當前大小總大小,下載狀態等。
登出和註冊downloadcontentobserver的程式碼

private static final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");
        getContentResolver().registerContentObserver(CONTENT_URI, true, observer);
        getContentResolver().unregisterContentObserver(observer);

定時任務中執行的runable 為更新UI的 updateView方法

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                updateView();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

最後即是updateView方法中傳送message的handeler程式碼,message可傳入兩個bundle引數和一個object物件。從handler中取出可以更新UI 或邏輯操作了,注意宣告handler時傳入了 mainlooper,這樣不用再特地主執行緒中更新UI,測試中下載完成時狀態碼為8,具體使用時可再次測試。下載結束後可將定時任務關閉置空。

    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull @NotNull Message msg) {
            super.handleMessage(msg);
            if (scheduledExecutorService != null) {
                int currentSize = msg.arg1;
                int totalSize = msg.arg2;
                Object object = msg.obj;
                tvDownload.setText(String.valueOf(((float) (currentSize / totalSize)) * 100));
                if ((Integer) object == 8) {
                    scheduledExecutorService.shutdownNow();
                    scheduledExecutorService = null;
                    Toast.makeText(ControlerActivity.this, "下載完成", Toast.LENGTH_SHORT).show();
                }
                Log.e(TAG, "handleMessage:" + currentSize + " " + totalSize + " " + object.toString());
            }
        }
    };

最後測試下載的本地的uri是否正確,檔案下載是否成功 Environment.getExternalStoragePublicDirectory獲取外部儲存的下載路徑。還有下載完成點選通知跳轉和下載完成監聽功能不再詳述。

  File file=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"/mobileqq_android.apk");
        if (file.exists()) {
            Log.e(TAG, "exists yes"+file.length());
        }else {
            Log.e(TAG, "exists no" );
        }

相關文章