概述

实现App常见下载公共 支持通知栏显示 支持 暂停、取消功能,使用Service、AsyncTask实现异步下载。特点简单、实用、方便源码扩展修改

详细

一、准备工作

1、项目运行环境AndroidStudio

3、通过Service、AsyncTask实现异步下载,并通过通知栏显示下载进度。

二、程序实现

1、工程目录

2、在AsyncTask中实现下载操作

public DownloadTask(DownloadListener listener) {
this.listener = listener;
}
/**
* 执行具体下载逻辑
* @param strings
* @return 下载状态
*/
@Override
protected Integer doInBackground(String... strings) {
InputStream is = null;
RandomAccessFile savedFile = null;
File file = null;
try {
long downloadedLength = 0; //记录已下载文件的长度
String downloadUrl = strings[0];//获取下载地址
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(directory + fileName);//将文件指定下载到SD卡的Download目录下
if (file.exists()) {//判断是否已存在文件,是:取出文件大小(字节数)
downloadedLength = file.length();
}
long contentLength = getContentLength(downloadUrl);//获取待下载文件大小(字节数)
if (contentLength == 0) {//长度为0,文件异常,下载失败
return TYPE_FAILED;
} else if (contentLength == downloadedLength) {//文件长度等于已下载长度,已下载完,直接返回成功
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("RANGE", "bytes=" + downloadedLength + "-")//断点下载,指定从哪个字节开始下载
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null) {
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
savedFile.seek(downloadedLength);//跳过已下载字节
byte[] bytes = new byte[1024];
int total = 0;
int len;
while ((len = is.read(bytes)) != -1) {
if (isCanceled) {//判断是否有取消操作
return TYPE_CANCELED;
} else if (isPaused) {//判断是否有暂停操作
return TYPE_PAUSED;
} else {
total += len;
savedFile.write(bytes, 0, len);
//计算已下载百分比
int progress = (int) ((total + downloadedLength) * 100 / contentLength);
publishProgress(progress);
}
}
} } catch (Exception e) {
e.printStackTrace();
} finally {
try { } catch (Exception e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
/**
* 更新下载进度
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
if (progress > lastProgress) {
listener.onProgress(progress);
lastProgress = progress;
}
}
/**
* 通知下载结果
* @param type
*/
@Override
protected void onPostExecute(Integer type) {
switch (type) {
case TYPE_SUCCESS:
listener.onSuccess();
break;
case TYPE_FAILED:
listener.onFailed();
break;
case TYPE_PAUSED:
listener.onPaused();
break;
case TYPE_CANCELED:
listener.onCanceled();
break;
default:
break;
}
} public void pauseDownload() {
isPaused = true;
} public void cancelDownload() {
isCanceled = true;
} private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(downloadUrl).build();
Response response = client.newCall(request).execute();
if (response != null && response.isSuccessful()) {
long contentLength = response.body().contentLength();
response.body().close();
return contentLength;
}
return 0;
}

3、使用interface观察者模式监听下载动作

public interface DownloadListener {
//当前下载进度
void onProgress(int progress); //下载成功
void onSuccess(); //下载失败
void onFailed(); //下载暂停
void onPaused(); //下载取消
void onCanceled();
}

4、在Service中初始化AsyncTask并实例化监听

private DownloadTask downloadTask;

private String downloadUrl;

private DownloadListener downloadListener = new DownloadListener() {//创建Download实例
@Override
public void onProgress(int progress) {
//构建显示下载进度的通知,并触发通知
getNotificationManager().notify(1, getNotification("Downloading ...",progress));
} @Override
public void onSuccess() {
downloadTask = null;
//下载成功将前台服务关闭,并创建一个下载成功的通知
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Success",-1));
Toast.makeText(DownloadService.this,"Download Success",Toast.LENGTH_SHORT).show(); } @Override
public void onFailed() {
downloadTask = null;
//下载失败将前台服务关闭,并创建一个下载失败的通知
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Failed",-1));
Toast.makeText(DownloadService.this,"Download Failed",Toast.LENGTH_SHORT).show(); } @Override
public void onPaused() {
downloadTask = null; Toast.makeText(DownloadService.this,"Download Paused",Toast.LENGTH_SHORT).show();
} @Override
public void onCanceled() {
downloadTask = null;
stopForeground(true);
Toast.makeText(DownloadService.this,"Download Canceled",Toast.LENGTH_SHORT).show();
}
};

5、创建下载进度通知栏

private Notification getNotification(String title, int progress) {
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher_round);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentIntent(pi);
builder.setContentTitle(title);
if (progress >= 0) {
//当progress大于或等0时才需要显示下载进度
builder.setContentText(progress + "%");
builder.setProgress(100, progress, false);
}
return builder.build();
}

6.创见DownloadBinder类,与外部建立连接

private DownloadBinder mBinder = new DownloadBinder();

@Override
public IBinder onBind(Intent intent) {
return mBinder;
} class DownloadBinder extends Binder{
public void startDownload(String url){
if (downloadTask == null){
downloadUrl = url;
downloadTask = new DownloadTask(downloadListener);
downloadTask.execute(downloadUrl);
startForeground(1,getNotification("Downloading ... ",0));
Toast.makeText(DownloadService.this,"Downloading ... ",Toast.LENGTH_SHORT).show();
}
} public void pauseDownload(){
if (downloadTask != null){
downloadTask.pauseDownload();
}else {
if (downloadUrl != null){
//取消下载是删除文件,关闭通知
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file = new File(directory + fileName);
if (file.exists()){
file.delete();
}
getNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this,"Canceled",Toast.LENGTH_SHORT).show();
}
}
} public void cancelDownload(){
if (downloadTask != null){
downloadTask.cancelDownload();
}
}
}

7、绑定Service

private DownloadService.DownloadBinder downloadBinder;

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBinder = (DownloadService.DownloadBinder) iBinder;
} @Override
public void onServiceDisconnected(ComponentName componentName) { }
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,DownloadService.class);
startService(intent);//启动服务
bindService(intent,serviceConnection,BIND_AUTO_CREATE);//绑定服务
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
//获取读写权限
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
}

三、运行效果

1、调用方法

public void startDownload(View view) {
if (downloadBinder == null){
return;
}
String url = "your URL (你的下载路径)";
downloadBinder.startDownload(url);
} public void pauseDownload(View view) {
if (downloadBinder == null){
return;
}
downloadBinder.pauseDownload(); } public void cancelDownload(View view) {
if (downloadBinder == null){
return;
}
downloadBinder.cancelDownload(); }

3、运行时的截图

四、其他补充

1、如何修改通知栏样式?

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher_round);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentIntent(pi);
builder.setContentTitle(title);

2、不了解Service、AsyncTask?

Android Service(服务)详解·(一)相关知识点引入

Android Service(服务)详解·(二)Service基本用法

3、还有疑问?

请联系我 留言 或者 E-mail : duyangs1994@gmail.com

一起学习,一起进步

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

Android异步下载的更多相关文章

  1. Android异步下载图片并且缓存图片到本地

    Android异步下载图片并且缓存图片到本地 在Android开发中我们经常有这样的需求,从服务器上下载xml或者JSON类型的数据,其中包括一些图片资源,本demo模拟了这个需求,从网络上加载XML ...

  2. Android异步下载网络图片

    最近新做的一个项目,里面需要下载网络上的图片,并显示在UI界面上,学Android有个常识,就是Android中在主线程中没法直接更新UI的,要想更新UI必须另外开启一个线程来实现,当开启的线程完成图 ...

  3. Android 异步下载

    package com.example.demo1; import java.io.File; import java.io.FileOutputStream; import java.io.IOEx ...

  4. Android多线程分析之五:使用AsyncTask异步下载图像

    Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<An ...

  5. Android多线程分析之一:使用Thread异步下载图像

    Android多线程分析之一:使用Thread异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处   打算整理一下对 Android F ...

  6. android AsyncTask异步下载并更新进度条

    AsyncTask异步下载并更新进度条    //如果不是很明白请看上篇文章的异步下载 AsyncTask<String, Integer, String> 第一个参数:String 传入 ...

  7. Android项目实战(三十一):异步下载apk文件并安装(非静默安装)

    前言: 实现异步下载apk文件 并 安装.(进度条对话框显示下载进度的展现方式) 涉及技术点: 1.ProgressDialog   进度条对话框  用于显示下载进度 2.AsyncTask     ...

  8. android开发步步为营之67:使用android开源项目android-async-http异步下载文件

    android-async-http项目地址 https://github.com/loopj/android-async-http.android-async-http顾名思义是异步的http请求, ...

  9. Android多线程分析之中的一个:使用Thread异步下载图像

    Android多线程分析之中的一个:使用Thread异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可.转载请注明出处 打算整理一下对 Android Fr ...

随机推荐

  1. php回溯

    $sl = debug_backtrace(); 返回的$sl 是一个二维数组 包含如下元素: function string 当前的函数名,参见: __FUNCTION__. line intege ...

  2. [转]Java 对象锁-synchronized()与线程的状态与生命周期

      线程的状态与生命周期 Java 对象锁-synchronized() ? 1 2 3 4 synchronized(someObject){   //对象锁 } 对象锁的使用说明: 1.对象锁的返 ...

  3. Tasker, Android系统增强神器, 变量汇总

    http://tasker.dinglisch.net/userguide_summary.html#variables.html http://tasker.dinglisch.net/usergu ...

  4. PEM DAC note

    开发指南V1.0库函数版本,PWM DAC实验 350页 STM32 的定时器最快的计数频率是72Mhz,8 为分辨率的时候,PWM 频率为72M/256=281.25Khz.如果是1阶RC滤波,则要 ...

  5. 电脑硬件天梯图—CPU、显卡、主板

    看到许多玩家对电脑的配置一点都不懂,这里特地制作了最新的硬件天梯图--CPU,显卡,主板,让大家对电脑硬件孰优孰劣有个一目了然的了解. 看不清楚的情点击小图看大图. 首先是CPU天梯图: 其次是显卡天 ...

  6. 计蒜之道 初赛 第三场 题解 Manacher o(n)求最长公共回文串 线段树

    腾讯手机地图 腾讯手机地图的定位功能用到了用户手机的多种信号,这当中有的信号的作用范围近.有的信号作用的范围则远一些.有的信号相对于用户在不同的方位强度是不同的,有的则是在不论什么一个方向上信号强度都 ...

  7. 判断openfire用户的状态

    /** * 判断openfire用户的状态 * 说明 :必须要 openfire加载 presence 插件,同时设置任何人都可以访问 * /status?jid=user1@my.openfire. ...

  8. [Android Pro] Android 必知必会-使用 supportV4 的 RoundedBitmapDrawable 实现圆角

    RoundedBitmapDrawable 是 supportV4 下的一个类,有了它,显示圆角和圆形图片的情况下就不需要额外的第三方类库了,还能和各种图片加载库配合使用. 背景 今天无意间看到一段实 ...

  9. mysql中避免使用保留字和关键字做列的名字

    设计数据表时,应尽量避免使用MySQL的关键字和保留字作为表名或列名. 比如key和keys为保留字,如果不小心使用关键字或者保留字作为列名字,执行下面的语句会出现语法错误: select * fro ...

  10. c#抓去网页

    c#利用WebClient和WebRequest获取网页源代码的比较 2011-11-28 10:26:42     我来说两句 收藏 我要投稿 C#中一般是可以利用WebClient类和WebReq ...