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

  • 相关阅读:
    macOS npm install -g npm失败提示无权限
    js中的展开运算符
    vite启动项目
    export与export default区别
    vue3新特性拾遗
    isRef, isReactive 与 isReadonly的原理
    shallowReactive与reactive的原理浅析
    shallowReadonly 与 readonly原理浅析
    shallowRef与ref的原理浅析
    设计模式-资料相关
  • 原文地址:https://www.cnblogs.com/demodashi/p/8491100.html
Copyright © 2020-2023  润新知