在github上面苦苦寻找都木有找到有关Android自带下载器DownloadManager多并发下载的,于是就决定自己试试写一个。
先上个图:
我的上篇文章
http://blog.csdn.net/u013277740/article/details/51737080
只实现了单个下载安装,这次升级一下
实现步骤:
1、初始化一个缓存线程池
cachedThreadPool = Executors.newCachedThreadPool();
2、在渲染每一条数据的时候往里面加任务
private void initView() {
mRvList.setLayoutManager(new LinearLayoutManager(this));
mDownloadAdapter = new CommonAdapter<Download>(this, R.layout.listitem_mul, mList) {
@Override
protected void convert(ViewHolder holder, final Download download, int position) {
holder.setText(R.id.tv_name, download.getName());
//往线程池中增加任务
cachedThreadPool.execute(new DownLoadTask(holder, download, position));
final NumberProgressBar numberBar = holder.getView(R.id.number_download);
holder.setOnClickListener(R.id.btn_install, new View.OnClickListener() {
@Override
public void onClick(View v) {
if (numberBar.getProgress() == 100) {
install(MulActivity.this, download.getLastDownLoadId());
}
}
});
}
};
mRvList.setAdapter(mDownloadAdapter);
}
3、更新UI
//转换到主线程
runOnUiThread(new Runnable() {
@Override
public void run() {
NumberProgressBar numberBar = holder.getView(R.id.number_download);
numberBar.setProgress(progress);
}
});
具体代码如下:
1、MulActivity
package com.zx.download;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import com.daimajia.numberprogressbar.NumberProgressBar;
import com.zhy.adapter.recyclerview.CommonAdapter;
import com.zhy.adapter.recyclerview.base.ViewHolder;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MulActivity extends AppCompatActivity {
public static final String DOWNLOAD_ID = "download_id";
@Bind(R.id.rv_list)
RecyclerView mRvList;
private DownloadChangeObserver downloadObserver;
public static final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");
private ExecutorService cachedThreadPool;
private List<Download> mList = new ArrayList<>();
private CommonAdapter mDownloadAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mul);
ButterKnife.bind(this);
initData();
cachedThreadPool = Executors.newCachedThreadPool();
}
private void initData() {
for (int i = 0; i < Contans.URLS.length; i++) {
Download download = new Download();
download.setFileName(Contans.FILE_NAMES[i]);
download.setName(Contans.NAMES[i]);
download.setTitle(Contans.TITLES[i]);
download.setDescription(Contans.DESCRIPTIONS[i]);
download.setDownLoadUrl(Contans.URLS[i]);
mList.add(download);
}
}
private void initView() {
mRvList.setLayoutManager(new LinearLayoutManager(this));
mDownloadAdapter = new CommonAdapter<Download>(this, R.layout.listitem_mul, mList) {
@Override
protected void convert(ViewHolder holder, final Download download, int position) {
holder.setText(R.id.tv_name, download.getName());
cachedThreadPool.execute(new DownLoadTask(holder, download, position));
final NumberProgressBar numberBar = holder.getView(R.id.number_download);
holder.setOnClickListener(R.id.btn_install, new View.OnClickListener() {
@Override
public void onClick(View v) {
if (numberBar.getProgress() == 100) {
install(MulActivity.this, download.getLastDownLoadId());
}
}
});
}
};
mRvList.setAdapter(mDownloadAdapter);
}
@OnClick(R.id.btn_downs)
public void onViewClicked() {
initView();
}
public class DownLoadTask implements Runnable {
private ViewHolder holder;
private Download downLoad;
private int position;
public DownLoadTask(ViewHolder holder, Download download, int position) {
this.holder = holder;
this.downLoad = download;
this.position = position;
}
@Override
public void run() {
downLoadFile(downLoad);
}
private void downLoadFile(Download downLoad) {
//1.得到下载对象
DownloadManager dowanloadmanager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
//2.创建下载请求对象,并且把下载的地址放进去
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downLoad.getDownLoadUrl()));
//3.给下载的文件指定路径
request.setDestinationInExternalFilesDir(MulActivity.this, Environment.DIRECTORY_DOWNLOADS, downLoad.getFileName());
//4.设置显示在文件下载Notification(通知栏)中显示的文字。6.0的手机Description不显示
request.setTitle(downLoad.getTitle());
request.setDescription(downLoad.getDescription());
//5更改服务器返回的minetype为android包类型
request.setMimeType("application/vnd.android.package-archive");
//6.设置在什么连接状态下执行下载操作
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
//7. 设置为可被媒体扫描器找到
request.allowScanningByMediaScanner();
//8. 设置为可见和可管理
request.setVisibleInDownloadsUi(true);
long lastDownloadId = dowanloadmanager.enqueue(request);
downLoad.setLastDownLoadId(lastDownloadId);
//10.采用内容观察者模式实现进度
downloadObserver = new DownloadChangeObserver(null, holder, downLoad, position);
getContentResolver().registerContentObserver(CONTENT_URI, true, downloadObserver);
}
}
//用于显示下载进度
class DownloadChangeObserver extends ContentObserver {
private ViewHolder holder;
private Download downLoad;
private int position;
public DownloadChangeObserver(Handler handler, ViewHolder holder, Download download, int position) {
super(handler);
this.holder = holder;
this.downLoad = download;
this.position = position;
}
@Override
public void onChange(boolean selfChange) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downLoad.getLastDownLoadId());
DownloadManager dManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final Cursor cursor = dManager.query(query);
if (cursor != null && cursor.moveToFirst()) {
final int totalColumn = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
final int currentColumn = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
int totalSize = cursor.getInt(totalColumn);
int currentSize = cursor.getInt(currentColumn);
float percent = (float) currentSize / (float) totalSize;
final int progress = Math.round(percent * 100);
runOnUiThread(new Runnable() {
@Override
public void run() {
NumberProgressBar numberBar = holder.getView(R.id.number_download);
numberBar.setProgress(progress);
}
});
Log.v("progress" + downLoad.getLastDownLoadId(), progress + "");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(downloadObserver);
}
//安装
private void install(Context context, long downloadId) {
Intent install = new Intent(Intent.ACTION_VIEW);
File apkFile = queryDownloadedApk(context, downloadId);
install.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
}
//通过downLoadId查询下载的apk,解决6.0以后安装的问题
public static File queryDownloadedApk(Context context, long downloadId) {
File targetApkFile = null;
DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloader.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
}
2、activity_mul.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zx.download.MainActivity">
<Button
android:id="@+id/btn_downs"
android:text="开始"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
3、Contans
package com.zx.download;
/**
* Created by user on 2017/6/19.
*/
public class Contans {
private static String NETURL_WEIXIN = "http://gdown.baidu.com/data/wisegame/8d5889f722f640c8/weixin_800.apk";
private static String NETURL_QQ = "http://gdown.baidu.com/data/wisegame/41a04ccb443cd61a/QQ_692.apk";
private static String NETURL_WANGYIYUN = "http://gdown.baidu.com/data/wisegame/9bfeb688ff2e0a19/wangyiyunyinle_95.apk";
private static String NETURL_KUWO = "http://gdown.baidu.com/data/wisegame/82337e10d2a72e61/kuwoyinle_8474.apk";
public static final String FILE_NAMES[] = {"weixin1.0.apk", "qq1.0.apk","wyy1.0.apk", "kuwo1.0.apk"};
public static final String NAMES[] = {"微信1.0", "QQ1.0","网易云音乐1.0", "酷我音乐1.0"};
public static final String URLS[] = {NETURL_WEIXIN, NETURL_QQ, NETURL_WANGYIYUN, NETURL_KUWO};
public static final String TITLES[] = {"weixin1.0", "qq1.0","wyy1.0", "kuwo1.0"};
public static final String DESCRIPTIONS[] = {"1、新增朋友圈","1、新增QQ空间","1、新增云音乐","1、新增酷我调频"};
}
4、Download
package com.zx.download;
import java.io.Serializable;
/**
* Created by user on 2017/6/19.
*/
public class Download implements Serializable {
private long lastDownLoadId;
//weixin.apk
private String fileName;
//weixin1.0
private String name;
private String downLoadUrl;
//通知栏
private String title;
private String description;
public String getDownLoadUrl() {
return downLoadUrl;
}
public void setDownLoadUrl(String downLoadUrl) {
this.downLoadUrl = downLoadUrl;
}
public long getLastDownLoadId() {
return lastDownLoadId;
}
public void setLastDownLoadId(long lastDownLoadId) {
this.lastDownLoadId = lastDownLoadId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
5、UpdataBroadcastReceiver
package com.zx.download;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import java.io.File;
import cn.trinea.android.common.util.PreferencesUtils;
/**
* Created by Administrator on 2016/9/20.
*/
public class UpdataBroadcastReceiver extends BroadcastReceiver {
@SuppressLint("NewApi")
public void onReceive(Context context, Intent intent) {
long downLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
long cacheDownLoadId = PreferencesUtils.getLong(context, MainActivity.DOWNLOAD_ID);
if (cacheDownLoadId == downLoadId) {
install(context);
}
}
private void install(Context context){
Intent install = new Intent(Intent.ACTION_VIEW);
File apkFile = queryDownloadedApk(context);
install.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
}
//通过downLoadId查询下载的apk,解决6.0以后安装的问题
public static File queryDownloadedApk(Context context) {
File targetApkFile = null;
DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
long downloadId = PreferencesUtils.getLong(context, MainActivity.DOWNLOAD_ID);
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloader.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
}
6、AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zx.download">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".UpdataBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
<activity android:name=".MulActivity">
</activity>
</application>
</manifest>
7、build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.zx.download"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.daimajia.numberprogressbar:library:1.4@aar'
compile 'cn.trinea.android.common:trinea-android-common:4.2.15'
compile('com.github.afollestad.material-dialogs:core:0.8.5.7@aar') {
transitive = true
}
compile 'com.zhy:base-rvadapter:3.0.3'
compile 'com.jakewharton:butterknife:7.0.1'
}
附加一个小功能,怎么判断所有的下载任务都完成了呢?
这里用SparseArray简单实现下
1、实例化一个SparseArray和计数变量
private SparseArray<Integer> progressArray = new SparseArray<>();
private int progressCount;
2、在downLoadFile方法中,把每个Key放进去,value都赋值为0
long lastDownloadId =dowanloadmanager.enqueue(request);
downLoad.setLastDownLoadId(lastDownloadId);
//把每个key放入map中
progressArray.put((int)lastDownloadId, 0);
3、在onChange方法中简单处理
float percent = (float) currentSize / (float) totalSize;
final int progress = Math.round(percent * 100);
runOnUiThread(new Runnable() {
@Override
public void run() {
NumberProgressBar numberBar = holder.getView(R.id.number_download);
numberBar.setProgress(progress);
if(progress == 100) {
int id = (int)downLoad.getLastDownLoadId();
if(progressArray.get(id) == 0) {
progressArray.put(id, progress);
Log.v("progress === " + downLoad.getLastDownLoadId(), progress + "");
progressCount ++;
if(progressCount == mList.size()) {
Log.v("progress === ","下载完成。。。。。。");
progressCount = 0;
}
}
}
}
});
最后附上Github源码:
https://github.com/MrXiong/MaterialDownLoad