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) ;
}
}
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();
}
}
}
效果圖如下