Android异步机制后台下载

Android异步机制后台下载

下载一个大文件

从最基础分析
要用到


功能分析

  • 开始下载
  • 暂停下载
  • 取消下载
  • 下载完成
  • 下载出错

状态监听

  • 下载的进度条更新()
  • 成功时
  • 失败时
  • 暂停时
  • 取消时

这里可以用一个接口定义5个状态,使用的时候重写监听方法,写入具体逻辑


实现

  • 下载需要后台,需要服务(需要和Activity互相传递消息,需要绑定服务)
  • 需要异步处理(网络请求和主线程中更新UI),不然会造成ANR

状态接口

public interface DownLoadListener {

    void onProgress(int progress);
    void onSuccess();
    void onFailed();
    void onPaused();
    void onCanceled();
}

异步下载任务

使用AsyncTask

传入参数是url,int数据处理进度,返回int类型的状态标记

public class DownLoadTask extends AsyncTask<String, Integer, Integer> {
 //定义四个下载状态标记
    private static final int TYPE_SUCXCSS = 0;
    private static final int TYPE_FAILED = 1;
    private static final int TYPE_PAUSED = 2;
    private static final int TYPE_CANCELED = 3;

    //这个监听在服务中重写,因为在服务中启动任务,可以顺便初始化DownLoadTask(构造函数)
    private DownLoadListener mListener;

    private boolean isPaused = false;//下载是否暂停标记
    private int lastProgress;//进度条上次更新时的大小
    private boolean isCancelled = false;


public DownLoadTask(DownLoadListener listener) {
        mListener = listener;
    }
}

重写doInBackground()

@Override
    protected Integer doInBackground(String... params) {

        InputStream is = null;//输入流
        RandomAccessFile savedFile = null;//用来访问那些保存数据记录的文件的,可以用seek( )方法来访问记录
        File file = null;//待会要下载的文件地址引用

        long downloadedLength = 0;//已下载的文件长度
        String downloadUrl = params[0];//传入参数的第一个就是url
        //subString(int)代表从该字符串的第int个开始截取(向右,下标第一个是0),lastIndexof(String)是String那里的字符数量(从左到右)
        String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
        //外置卡的共享目录路径,这里要得是下载目录,平时手机里的Download文件夹
        String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
        //文件路径加文件名
        file = new File(directory + fileName);
        if (file.exists()) {
            //如果文件已经存在,则查看已下载文件长度
            downloadedLength = file.length();
        }
        long contentLength = getContentLength(downloadUrl);//获取要文件的大小长度
        if (contentLength == 0) {
            //如果文件长度是0,说明文件有问题,返回错误标记
            return TYPE_FAILED;
        } else if (contentLength == downloadedLength) {
            //如果文件长度是downloadedLength,说明文件下完了,返回完成标记
            return TYPE_SUCXCSS;
        }

        OkHttpClient client = new OkHttpClient();//下载客户端
        Request request = new Request.Builder()//构建下载请求
                .addHeader("RANGE", "bytes=" + downloadedLength + "-")
                .url(downloadUrl).build();
        try {
            Response response = client.newCall(request).execute();//发出请求并接受
            if (response != null) {
                //收到响应,则返回response读到的输入流给本地输入流is
                is = response.body().byteStream();
                savedFile = new RandomAccessFile(file, "rw");//随机读写模式为读写,写入file中
                //跳过已下载的字节
                savedFile.seek(downloadedLength);


                byte[] b = new byte[1024];//常规读写文件形式,缓冲buffer
                int total = 0;//读到的总下载长度,此处用他们来计算出进度条大小
                int len;//读到的文件长度

                while ((len = is.read(b)) != -1) {
                    //is.read(d)是len(buffer)的长度,等于-1代表到达终点
                    if (isCancelled) {
                        //如果取消了则返回取消标记
                        return TYPE_CANCELED;
                    } else if (isPaused) {
                        return TYPE_PAUSED;
                    } else {
                        total += len;
                        savedFile.write(b, 0, len);//写len长度b数组的byte到文件中
                        int progress = (int) ((total + downloadedLength) * 100 / contentLength);
                        publishProgress(progress);//发出进度条
                    }
                }
                //关闭response响应
                response.body().close();
                return TYPE_SUCXCSS;

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    //关输入流
                    is.close();
                }
                if (savedFile != null) {
                    //关闭随机读取
                    savedFile.close();
                }
                if (isCancelled() && file != null) {
                    file.delete();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return TYPE_FAILED;
    }

获取内容长度的方法

/**
     * 如果请求不为空并且响应成功接收到(就是以前的响应码200),就把文件的长度返回
     * 如果请求为空或者响应没接收到,返回1
     */
    private long getContentLength(String url) {

        OkHttpClient client = new OkHttpClient();//实例化客户端
        Request request = new Request.Builder()
                .url(url)
                .build();//构造请求体

        try {
            Response response = client.newCall(request).execute();//发出请求
            if (request != null && response.isSuccessful()) {
                //如果请求不为空并且响应成功接收到(就是以前的响应码200),就把文件的长度返回
                long contentLength = response.body().contentLength();
                response.close();
                return contentLength;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //如果请求为空或者响应没接收到,返回0
        return 0;
    }

剩下的是(最后加的)。。。。。

public void pauseDownload() {
        //只需修改状态标记
        isPaused = true;
    }
    public void cancelDownload() {
        //只需修改状态标记
        isCancelled = true;
    }

onProgressUpdate()方法

@Override
    protected void onProgressUpdate(Integer... values) {
        int progress = values[0];
        if (progress > lastProgress) {
            mListener.onProgress(progress);
            lastProgress = progress;
        }
    }

onPostExecute()方法

@Override
    protected void onPostExecute(Integer status) {
        switch (status) {
            case TYPE_SUCXCSS:
                mListener.onSuccess();
                break;
            case TYPE_FAILED:
                mListener.onFailed();
                break;
            case TYPE_PAUSED:
                mListener.onPaused();
                break;
            case TYPE_CANCELED:
                mListener.onCanceled();
                break;
        }
    }

后台服务

新建DownloadService服务

public class DownloadService extends Service {
    private static final String TAG = "DownloadService";
    private DownLoadTask mDownLoadTask;
    private String downloadUrl;
    private DownloadBinder mBinder = new DownloadBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

     /**
     * 获取系统状态栏信息服务
     */
    private NotificationManager getNotificationManager() {
        return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    /**
     *  显示进度封装
     * */
    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);
        //设置notification信息
        builder.setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setContentIntent(pi)
                .setContentTitle(title);
        if (progress >= 0) {
            //当Progress大于等于0时才显示进度
            builder.setContentText(progress + "%");
            builder.setProgress(100, progress, false);
        }
        return builder.build();
    }

}

内部类DownloadBinder

 public class DownloadBinder extends Binder {

        /**
         *  开始下载
         * */
        public void startDownLoad(String url) {
            if (mDownLoadTask == null) {
                downloadUrl = url;
                mDownLoadTask = new DownLoadTask(listener);
                mDownLoadTask.execute(downloadUrl);//开始异步任务,传入url
                startForeground(1, getNotification("下载中...", 0));
                Log.d(TAG, "开始下载的服务");
            }
        }

        public void pauseDownLoad(){
            if (mDownLoadTask != null){
                mDownLoadTask.pauseDownload();
            }
        }

        public void cancelDownLoad(){
            if (mDownLoadTask != null){
                mDownLoadTask.cancelDownload();
            }else{
                if(downloadUrl != null){

                    //下面三句是为了获取文件名字,然后对比手机存储内的,删除
                   String filename = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
                    String derectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
                    File file = new File(derectory + filename);
                    if(file.exists()){
                        file.delete();
                    }
                    getNotificationManager().cancel(1);//关闭1号通知
                    stopForeground(true);
                    Log.d(TAG, "取消了");
                }
            }
        }

重写监听方法

private DownLoadListener listener = new DownLoadListener() {
        @Override
        public void onProgress(int progress) {
            //设置进度条
            getNotificationManager().notify(1, getNotification("下载中....", progress));
        }

        @Override
        public void onSuccess() {
            mDownLoadTask = null;
            //关闭前台服务,并且创建下载成功通知
            stopForeground(true);
            getNotificationManager().notify(1, getNotification("下好了亲", -1));
            Log.d(TAG, "下载wan");
        }

        @Override
        public void onFailed() {
            mDownLoadTask = null;
            stopForeground(true);
            getNotificationManager().notify(1, getNotification("xiazai出错", -1));
            Log.d(TAG, "下载出错");
        }

        @Override
        public void onPaused() {
            mDownLoadTask = null;
            getNotificationManager().notify(1, getNotification("xiazai暂停", -1));
            Log.d(TAG, "下载暂停");
        }

        @Override
        public void onCanceled() {
            mDownLoadTask = null;
            stopForeground(true);
            getNotificationManager().notify(1, getNotification("xiazai取消了", -1));
            Log.d(TAG, "下载取消了");
        }
    };

控制的活动MainActivity

纽带ServiceConnection

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = (DownloadService.DownloadBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

onCreate中

        initView();

        Intent intent = new Intent(this, DownloadService.class);
        startService(intent);
        bindService(intent, mConnection, 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);
        } else {
            //权限够了这里处理逻辑
            Log.d(TAG, "权限够了");
        }
```
Activity中获取权限的回调
```
//获取到权限回调方法
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //权限够了处理逻辑
                    Log.d(TAG, "权限够了,逻辑");
                } else {
                    Toast.makeText(this, "权限不够,程序将退出", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
                break;
        }
```
按钮的控制
```
@Override
    public void onClick(View v) {
        if (mBinder == null) {
            //没纽带,控制个毛,直接GG
            return;
        }
        switch (v.getId()) {
            case R.id.button:
                String url = "http://down.360safe.com/se/360se8.1.1.250.exe";
                mBinder.startDownLoad(url);
                break;
            case R.id.button2:
                mBinder.pauseDownLoad();
                break;
            case R.id.button3:
                mBinder.cancelDownLoad();
                break;
            default:
                break;
        }
    }
```

![布局](http://upload-images.jianshu.io/upload_images/3515789-80b1777e35111901.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

注册服务与权限
```
<uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```

```
<service android:name=".service.DownloadService"/>
```
---

Github源代码**[DownLoadService](https://github.com/minminaya/DownLoadService)**

参考自郭霖《第一行代码》
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值