美文网首页
实战项目-2 - Android异步机制后台下载

实战项目-2 - Android异步机制后台下载

作者: minminaya | 来源:发表于2017-03-19 19:52 被阅读1088次

    下载一个大文件

    从最基础分析
    要用到


    功能分析

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

    状态监听

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

    这里可以用一个接口定义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;
            }
        }
    ```
    
    ![布局](https://img.haomeiwen.com/i3515789/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)**
    
    参考自郭霖《第一行代码》

    相关文章

      网友评论

          本文标题:实战项目-2 - Android异步机制后台下载

          本文链接:https://www.haomeiwen.com/subject/kzymnttx.html