Android 文件下載

1、開啓一個服務Service實現後臺下載

2、通過線程池管理器實現並行下載數量控制

3、利用廣播機制更新UI

4、利用網絡請求庫Ohttp3進行下載


創建一個下載任務類

/**
 * Created by familylove on 2017/4/24.
 * 下載文件任務類
 */

public class DownloadFileTask implements Callable<String> {

    private File mDownloadFile;
    private String mFileSize;
    private String mFilePath;
    private String mFileName;
    private String mDownloadUrl;
    private boolean mPause = false;
    private DownloadListener mDownloadListener;


    public DownloadFileTask(String fileName, String downloadUrl, String filePath, DownloadListener downloadListener) {

        this.mFilePath = filePath;
        this.mFileName = fileName;
        this.mDownloadUrl = downloadUrl;
        this.mDownloadListener = downloadListener;

    }

    @Override
    public String call() throws Exception {

        if (TextUtils.isEmpty(mFilePath) || TextUtils.isEmpty(mFileName))
            return "path or name is empty";
        char tempEnd = mFilePath.charAt(mFilePath.length() - 1);
        if (tempEnd == '/') {
            mDownloadFile = new File(mFilePath + mFileName + ".temp");
        } else {
            mDownloadFile = new File(mFilePath + File.separator + mFileName + ".temp");
        }
        if (mDownloadFile.exists()) {
            long size = mDownloadFile.length();
            mFileSize = "bytes=" + size + "-";
        }
        //設置輸出流.
        OutputStream outPutStream = null;
        InputStream inputStream = null;
        try {
            OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
            Request request = null;
            if (!TextUtils.isEmpty(mFileSize)) {
                request = new Request.Builder().header("Range", mFileSize).url(mDownloadUrl).build();
            } else {
                request = new Request.Builder().url(mDownloadUrl).build();
            }
            Call call = okHttpClient.newCall(request);

            //檢測是否支持斷點續傳
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            String responeRange = response.headers().get("Content-Range");
            if (responeRange == null || !responeRange.contains(Long.toString(mDownloadFile.length()))) {
                //最後的標記爲 true 表示下載的數據可以從上一次的位置寫入,否則會清空文件數據.
                outPutStream = new FileOutputStream(mDownloadFile, false);
            } else {
                outPutStream = new FileOutputStream(mDownloadFile, true);
            }

            inputStream = responseBody.byteStream();
            //如果有下載過的歷史文件,則把下載總大小設爲 總數據大小+文件大小 . 否則就是總數據大小
            if (TextUtils.isEmpty(mFileSize)) {
                if (mDownloadListener != null)
                    mDownloadListener.downloadTotalSize(mDownloadUrl, responseBody.contentLength());
            } else {
                if (mDownloadListener != null)
                    mDownloadListener.downloadTotalSize(mDownloadUrl, responseBody.contentLength() + mDownloadFile.length());
            }

            int length;
            //設置緩存大小
            byte[] buffer = new byte[1024];

            //開始寫入文件
            while ((length = inputStream.read(buffer)) != -1) {

                if (!mPause) {
                    outPutStream.write(buffer, 0, length);
                    if (mDownloadListener != null)
                        mDownloadListener.onDownloadSize(mDownloadUrl, mDownloadFile.length());
                } else {
                    return null;
                }
            }
        } catch (Exception e) {
            if (mDownloadListener!=null)
                mDownloadListener.downloadFail(mDownloadUrl);
            return null ;
        } finally {
            if (outPutStream != null) {//清空緩衝區
                outPutStream.flush();
                outPutStream.close();
            }
            if (inputStream != null)
                inputStream.close();
        }

        //下載後重命名
        mDownloadFile.renameTo(new File(mFilePath+File.separator+mFileName));
        //下載完成
        if (mDownloadListener != null)
            mDownloadListener.onComplete(mDownloadUrl);
        return null;
    }

    //停止下載
    public void setPause(boolean pause) {

        this.mPause = pause;

    }

    public interface DownloadListener {

        public void downloadTotalSize(String tag, long totalSize);

        public void onDownloadSize(String tag, long downloadFileSize);

        public void onComplete(String tag);

        public void downloadFail(String tag) ;

    }
}


創建一個服務 DownloadFileService

public class DownloadFileService extends Service {

    private static final int MAX_DOWNLOAD_TASK = 2; //最大的下載任務
    private LocalBroadcastManager mLocalBroadcastManager;
    private OCThreadExecutor mOcThreadExecutor;
    private Map<String, DownloadBean> mAllDownloadTask;  //任務管理列表
    private int mRunningThread = 0;
    private Map<String, DownloadFileTask> mDownloadFileTasks;


    // 文件下載狀態
    public static final int DOWNLOAD_WRITING = 5;  //下載等待
    public static final int DOWNLOAD_LOADING = 1;  // 正在下載
    public static final int DOWNLOAD_PAUSE = 2;    // 下載暫停
    public static final int DOWNLOAD_FAIL = 3;      // 下載失敗
    public static final int DOWNLOAD_SUCCESS = 4; // 下載成功


    //更新所有的
    public static final String DOWNLOAD_UPLOAD_ALL = "com.wb.download.update.all";
    //更新一個
    public static final String DOWNLOAD_UPLOAD_SINGLE = "com.wb.download.update.single";
    //添加任務
    public static final String DOWNLOAD_NEW_TASK = "com.wb.download.new.task";

    public static final String DOWNLOAD_NAME = "download_name";
    public static final String DOWNLOAD_PATH = "download_path";
    public static final String DOWNLOAD_URL = "download_url";

    public DownloadFileService() {

    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (mAllDownloadTask == null)
            mAllDownloadTask = new HashMap<>();
        if (mDownloadFileTasks == null)
            mDownloadFileTasks = new HashMap<>();
        if (mLocalBroadcastManager == null)
            mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        if (mOcThreadExecutor == null)
            mOcThreadExecutor = new OCThreadExecutor(MAX_DOWNLOAD_TASK, "file_download_pool");

    }

    @Override
    public IBinder onBind(Intent intent) {

        return new DownLoadBind();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {


        String downloadName = "";
        String downloadFilePath = "";
        String downloadUrl = "";

        if (intent == null || intent.getAction() == null)
            return super.onStartCommand(intent, flags, startId);

        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            return super.onStartCommand(intent, flags, startId);
        }

        if (action.equals(DOWNLOAD_NEW_TASK)) {

            Bundle bundle = intent.getExtras();
            if (bundle == null) {
                return super.onStartCommand(intent, flags, startId);
            }

            downloadName = bundle.getString(DOWNLOAD_NAME);
            downloadUrl = bundle.getString(DOWNLOAD_URL);
            downloadFilePath = bundle.getString(DOWNLOAD_PATH);
            checkTask(downloadName, downloadUrl, downloadFilePath);
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 開始下一個下載任務
     * 這個下載任務處於等待狀態
     *
     * @param downloadBean
     */
    private void startNextTask(DownloadBean downloadBean) {

        if (downloadBean == null)
            return;
        File downloadFile = new File(downloadBean.getPath() + File.separator + downloadBean.getName());
        if (!downloadFile.exists()) {
            switch (downloadBean.getStatus()) {
                case DOWNLOAD_WRITING:
                    startDownloadTask(downloadBean);
                    break;
            }
        }
    }

    //檢查預加載下載任務
    private void checkTask(String downloadName, String downloadUrl, String downloadFilePath) {

        if (TextUtils.isEmpty(downloadFilePath) || TextUtils.isEmpty(downloadName) || TextUtils.isEmpty(downloadUrl)) {
            return;
        }
        if (downloadFilePath.charAt(downloadFilePath.length() - 1) == '/') {
            downloadFilePath = downloadFilePath.substring(0, downloadFilePath.length() - 1);
        }

        File teFile = new File(downloadFilePath);
        if (!teFile.exists()) {
            boolean flag1 = teFile.mkdir();
            if (!flag1) {
                teFile.mkdirs();
            }
        }

        DownloadBean downloadBean = null;
        File downloadFile = new File(downloadFilePath + File.separator + downloadName);
        if (downloadFile.exists()) {  // 已經下載了

        } else {

            if (mAllDownloadTask.containsKey(downloadUrl)) {
                downloadBean = mAllDownloadTask.get(downloadUrl);
                switch (downloadBean.getStatus()) {
                    case DOWNLOAD_FAIL:
                    case DOWNLOAD_PAUSE:
                        downloadBean.setStatus(DOWNLOAD_WRITING);
                        startDownloadTask(downloadBean);
                        break;
                }
            } else {
                downloadBean = new DownloadBean(downloadName, downloadFilePath, downloadUrl);
                downloadBean.setStatus(DOWNLOAD_WRITING);
                mAllDownloadTask.put(downloadUrl, downloadBean);
                //開始下載
                startDownloadTask(downloadBean);
            }
        }
    }

    /**
     * 開始下載
     *
     * @param downloadBean
     */
    public void startDownloadTask(DownloadBean downloadBean) {

        if (downloadBean == null)
            return;
        if (mRunningThread < MAX_DOWNLOAD_TASK) {
            mRunningThread++;

            DownloadFileTask downloadFileTask = new DownloadFileTask(downloadBean.getName(), downloadBean.getUrl(), downloadBean.getPath(), mDownloadListener);
            mDownloadFileTasks.put(downloadBean.getUrl(), downloadFileTask);
            downloadBean.setStatus(DOWNLOAD_LOADING);
            mAllDownloadTask.put(downloadBean.getUrl(), downloadBean);
            mOcThreadExecutor.submit(downloadFileTask,downloadBean.getUrl());
        }
        if (downloadBean.getStatus() == DOWNLOAD_PAUSE || downloadBean.getStatus() == DOWNLOAD_FAIL) {
            downloadBean.setStatus(DOWNLOAD_WRITING);
            mAllDownloadTask.put(downloadBean.getUrl(), downloadBean);
        }
        updateDownloadList();
    }

    //停止下載任務
    public void stopDownloadTask(DownloadBean downloadBean) {

        if (downloadBean == null)
            return;
        if (downloadBean.getStatus() == DOWNLOAD_LOADING) {

            mRunningThread = mRunningThread - 1;
            mDownloadFileTasks.get(downloadBean.getUrl()).setPause(true);
            downloadBean.setStatus(DOWNLOAD_PAUSE);
            mAllDownloadTask.put(downloadBean.getUrl(), downloadBean);
            mOcThreadExecutor.removeTag(downloadBean.getUrl()) ;
            mDownloadFileTasks.remove(downloadBean.getUrl()) ;
        }
        updateDownloadList();
    }


    public List<DownloadBean> getDownloadBeanList() {

        if (mAllDownloadTask != null)
            return new ArrayList<>(mAllDownloadTask.values());
        else
            return null;
    }

    // 更新列表
    private void updateDownloadList() {

        if (mLocalBroadcastManager != null) {
            mLocalBroadcastManager.sendBroadcast(new Intent(DOWNLOAD_UPLOAD_ALL));
            return;
        }
    }


    //更新一個
    private void updateItem(DownloadBean downloadBean) {

        if (downloadBean != null && downloadBean.getTotalSize() > 0) {

            if (mLocalBroadcastManager == null)
                return;
            int progressBarLength = (int) (((float) downloadBean.getDownloadSize() / downloadBean.getTotalSize()) * 100);
            Intent intent = new Intent(DOWNLOAD_UPLOAD_SINGLE);
            intent.putExtra("progressBarLength", progressBarLength);
            intent.putExtra("downloadedSize", String.format("%.2f", downloadBean.getDownloadSize() / (1024.0 * 1024.0)));
            intent.putExtra("totalSize", String.format("%.2f", downloadBean.getTotalSize() / (1024.0 * 1024.0)));
            intent.putExtra("item", downloadBean);

            mLocalBroadcastManager.sendBroadcast(intent);
        }
    }

    public void downloadComplete(DownloadBean downloadBean) {

        if (mLocalBroadcastManager != null) {
            mLocalBroadcastManager.sendBroadcast(new Intent(DOWNLOAD_UPLOAD_ALL));
            return;
        }


    }

    private DownloadBean startNextDownloadBean() {

        if (mAllDownloadTask != null) {
            for (DownloadBean bean : mAllDownloadTask.values()) {
                if (bean.getStatus() == DOWNLOAD_WRITING) {
                    return bean;
                }
            }
        }
        return null;
    }


    //下載監聽
    private DownloadFileTask.DownloadListener mDownloadListener = new DownloadFileTask.DownloadListener() {

        @Override
        public void downloadTotalSize(String tag, long totalSize) {
            mAllDownloadTask.get(tag).setTotalSize(totalSize);
        }

        @Override
        public void onDownloadSize(String tag, long downloadFileSize) {
            mAllDownloadTask.get(tag).setDownloadSize(downloadFileSize);
            updateItem(mAllDownloadTask.get(tag));
        }

        @Override
        public void onComplete(String tag) {

            mRunningThread = mRunningThread - 1;
            mDownloadFileTasks.remove(tag);
            mOcThreadExecutor.removeTag(tag);
            mAllDownloadTask.get(tag).setStatus(DOWNLOAD_SUCCESS);
            if (mAllDownloadTask != null) {
                startNextTask(startNextDownloadBean());
            }
            downloadComplete(mAllDownloadTask.get(tag));
        }

        @Override
        public void downloadFail(String tag) {

            mRunningThread = mRunningThread - 1;
            mDownloadFileTasks.remove(tag);
            mOcThreadExecutor.removeTag(tag);
            mAllDownloadTask.get(tag).setStatus(DOWNLOAD_FAIL);
            updateDownloadList();
        }
    };


    public class DownLoadBind extends Binder {

        public DownloadFileService getDownloadFileService() {
            return DownloadFileService.this;
        }
    }

}


在UI界面中通過 BroadcastReceiver    mUpdateBroadcastReceiver 刷新界面 

 

public class DownloadFileActivity extends AppCompatActivity {


    private RecyclerView mRecycleViewDownload;
    private OCDownloadAdapter mOcDownloadAdapter ;
    private LocalBroadcastManager mLocalBroadcastManager ;
    private ServiceConnection mServiceConnection ;
    private DownloadFileService mDownloadFileService ;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        mRecycleViewDownload = (RecyclerView) findViewById(R.id.recyclerDownload);
        LinearLayoutManager layoutManager = new LinearLayoutManager(DownloadFileActivity.this) ;
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecycleViewDownload.setLayoutManager(layoutManager);
        mOcDownloadAdapter = new OCDownloadAdapter(DownloadFileActivity.this) ;
        mOcDownloadAdapter.setOnRecyclerViewClick(mOnRecyclerViewClick);
        mRecycleViewDownload.setAdapter(mOcDownloadAdapter);


        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this) ;

        IntentFilter intentFilter = new IntentFilter() ;
        intentFilter.addAction(DownloadFileService.DOWNLOAD_UPLOAD_ALL);
        intentFilter.addAction(DownloadFileService.DOWNLOAD_UPLOAD_SINGLE);
        mLocalBroadcastManager.registerReceiver(mUpdateBroadcastReceiver,intentFilter);

        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mDownloadFileService =  ((DownloadFileService.DownLoadBind)service).getDownloadFileService() ;
                mOcDownloadAdapter.updateAllItem(mDownloadFileService.getDownloadBeanList());
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

                if (mLocalBroadcastManager!=null){
                    mLocalBroadcastManager.unregisterReceiver(mUpdateBroadcastReceiver);
                    return;
                }
            }
        } ;

        startService(new Intent(DownloadFileActivity.this,DownloadFileService.class)) ;
        bindService(new Intent(DownloadFileActivity.this,DownloadFileService.class),mServiceConnection,BIND_AUTO_CREATE) ;

        if (mDownloadFileService!=null){
            mOcDownloadAdapter.updateAllItem(mDownloadFileService.getDownloadBeanList());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mLocalBroadcastManager!=null)
            mLocalBroadcastManager.unregisterReceiver(mUpdateBroadcastReceiver);
        unbindService(mServiceConnection);
    }

    private OnRecyclerViewClick<DownloadBean> mOnRecyclerViewClick = new OnRecyclerViewClick<DownloadBean>() {
        @Override
        public void onClick(DownloadBean object) {

            if (object==null)
                return;
            if (object.getStatus() == DownloadFileService.DOWNLOAD_LOADING){
                mDownloadFileService.stopDownloadTask(object);
            }else {
                mDownloadFileService.startDownloadTask(object);
            }

        }

        @Override
        public boolean onLongClick(DownloadBean object) {
            return false;
        }
    } ;

    private BroadcastReceiver mUpdateBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            switch (intent.getAction()){
                case DownloadFileService.DOWNLOAD_UPLOAD_ALL:
                    mOcDownloadAdapter.updateAllItem(mDownloadFileService.getDownloadBeanList());
                    break;
                case DownloadFileService.DOWNLOAD_UPLOAD_SINGLE:

                    DownloadBean bean = (DownloadBean) intent.getExtras().getSerializable("item") ;
                    String downloadedSize = intent.getExtras().getString("downloadedSize");
                    String totalSize = intent.getExtras().getString("totalSize");
                    int progressLength = intent.getExtras().getInt("progressBarLength");
                    if (bean!=null){
                        if (mOcDownloadAdapter.getItemPosition(bean)>=0){
                            View itemView = mRecycleViewDownload.getChildAt(mOcDownloadAdapter.getItemPosition(bean));
                            if (itemView==null){
                                Log.e("leBrace","itemView is empty") ;
                            }else {
                                TextView tvPress = (TextView)itemView.findViewById(R.id.tvProgress) ;
                                ProgressBar progressBar = (ProgressBar)itemView.findViewById(R.id.progressBar) ;
                                progressBar.setProgress(progressLength);
                                //更新文字進度
                                tvPress.setText(downloadedSize+"MB / "+totalSize+"MB");

                                TextView tvDownloadFlag = (TextView)itemView.findViewById(R.id.tvDownloadFlag) ;
                                switch (bean.getStatus()){
                                    case DownloadFileService.DOWNLOAD_LOADING:
                                        tvDownloadFlag.setText("下載中");
                                        break;
                                    case DownloadFileService.DOWNLOAD_FAIL:
                                        tvDownloadFlag.setText("下載失敗");
                                        break;
                                    case DownloadFileService.DOWNLOAD_SUCCESS:
                                        tvDownloadFlag.setText("下載成功");
                                        break;
                                    case DownloadFileService.DOWNLOAD_WRITING:
                                        tvDownloadFlag.setText("下載等待") ;
                                        break;
                                }
                            }

                        }else {
                            //Toast.makeText(context,"",Toast.LENGTH_SHORT).show();
                        }
                    }
                    break;
            }
        }
    } ;
}

 線程池管理類


/**
 * Created by familylove on 2017/4/20.
 * 線程池管理
 */
public class OCThreadExecutor extends  ThreadPoolExecutor{

    private static LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(10) ;
    private Map<String,Callable> mMapRunnable ;

    public OCThreadExecutor(int maxDownloadSize,String poolName) {
        super(maxDownloadSize, maxDownloadSize, 0L, TimeUnit.SECONDS, workQueue, new OCThreadFactory(poolName));
        mMapRunnable = new HashMap<>() ;
    }


    //執行
    public void submit(Callable callable , String taskTag){

        synchronized (this){
            if (TextUtils.isEmpty(taskTag))
                return;
            if (!mMapRunnable.containsKey(taskTag)){
                mMapRunnable.put(taskTag,callable) ;
                submit(callable) ;
            }
        }
    }


    //移除隊列
    public boolean removeTag(String taskTag){
        boolean flag = false ;
        if (TextUtils.isEmpty(taskTag))
            return  flag;
        if (mMapRunnable.containsKey(taskTag)){

            if (mMapRunnable.remove(taskTag)!=null){
                flag =  true ;
            }else {
                flag =  false ;
            }
        }
        return flag ;
    }


    static class OCThreadFactory implements ThreadFactory{

        private final String name ;

        public OCThreadFactory(String name) {
            this.name = name ;
        }

        public String getPoolName(){
            return this.name ;
        }

        @Override
        public Thread newThread(@NonNull Runnable r) {
            return new OCThread(r,name);
        }
    }

    static class OCThread extends Thread{

        public OCThread(Runnable runnable,String name){
            super(runnable,name);
            setName(name);
        }
        @Override
        public void run() {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
            super.run();
        }
    }
}

效果圖如下







 





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章