Android异步下载

概述

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

详细

一、准备工作

1、项目运行环境AndroidStudio

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

二、程序实现

1、工程目录

image.png

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、运行时的截图

1504094492175.jpg

四、其他补充

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大师发表,拒绝转载,转载需要作者授权

原文地址:https://www.cnblogs.com/demodashi/p/8491100.html