第一行代码读书笔记 10-- 探究服务(下)

作者: 开心wonderful | 来源:发表于2017-01-22 22:38 被阅读315次

    本篇文章主要介绍以下几个知识点:

    • 服务的实践应用。
    图片来源于网络

    10.5 服务的最佳实践——完整版的下载示例

    本节来综合运用一下,实现一个在服务中经常使用到的功能——下载。效果如下:

    下载示例效果

    首先添加网络请求依赖库:

    compile 'com.squareup.okhttp3:okhttp:3.5.0'
    

    接下来需要定义一个回调接口,用于对下载过程中的各种状态进行监听和回调,新建 DownloadListener 接口:

    /**
     * Function:回调接口,对下载过程中的各种状态进行监听和回调
     * Author:kxwon on 2017/1/22 19:09
     * Email:kxwonder@163.com
     */
    
    public interface DownloadListener {
        
        void onProgress(int progress);   // 下载进度
        
        void onSuccess();     // 下载成功
        
        void onFailed();     // 下载失败
        
        void onPaused();     // 下载暂停
        
        void onCanceled();   // 下载取消
    }
    

    回调接口定义好后,下面就开始编写下载功能了,这里用 AsyncTask 来实现,新建一个 DownloadTask 继承自 AsyncTask:

    public class DownloadTask extends AsyncTask<String,Integer,Integer>{
    
        public static final int TYPE_SUCCESS = 0;  // 下载成功
        public static final int TYPE_FAILED = 1;   // 下载失败
        public static final int TYPE_PAUSED = 2;   // 下载暂停
        public static final int TYPE_CANCELED = 3; // 下载取消
    
        private DownloadListener listener;
    
        private boolean isCanceled = false;
    
        private boolean isPaused = false;
    
        private int lastProgress;//上一次的下载进度
    
        public DownloadTask(DownloadListener listener){
            this.listener = listener;
        }
    
        /**
         * 用于在后台执行具体的下载逻辑
         * @param params
         * @return
         */
        @Override
        protected Integer doInBackground(String... params) {
            InputStream is = null;
            RandomAccessFile savedFile = null;
            File file = null;
            try{
                long downloadedLength = 0;// 记录已下载的文件长度
                String downloadUrl = params[0];//获取到下载的URL地址
                String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));//下载的文件名
                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){
                    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[] b = new byte[1024];
                    int total = 0;
                    int len;
                    while ((len = is.read(b)) != -1){
                        if (isCanceled){
                            return TYPE_CANCELED;
                        }else if (isPaused){
                            return TYPE_PAUSED;
                        }else {
                            total += len;
                            savedFile.write(b,0,len);
                            // 计算已下载的百分比
                            int progress = (int)((total + downloadedLength) * 100/contentLength);
                            publishProgress(progress);//通知当前的下载进度
                        }
                    }
                    response.body().close();
                    return TYPE_SUCCESS;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if (is != null){
                        is.close();
                    }
                    if (savedFile != null){
                        savedFile.close();
                    }
                    if (isCanceled && file != null){
                        file.delete();
                    }
                }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 integer
         */
        @Override
        protected void onPostExecute(Integer integer) {
            switch (integer){
                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;
        }
    
        /**
         * 获取待下载文件的总长度
         * @param downloadUrl
         * @return
         */
        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.close();
                return contentLength;
            }
            return 0;
        }
    }
    

    以上就把具体的下载功能完成了,下面为了保证 DownloadTask 可以一直在后台运行,还需创建一个下载的服务。新建 DownloadService 如下:

    public class DownloadService extends Service {
    
        private DownloadTask downloadTask;
    
        private String downloadUrl;
    
        private DownloadListener listener = new DownloadListener() {
            @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));
                ToastUtils.showShort("下载成功");
            }
    
            @Override
            public void onFailed() {
                downloadTask = null;
                // 下载失败时将前台服务通知关闭,并创建一个下载失败的通知
                stopForeground(true);
                getNotificationManager().notify(1,getNotification("Download Failed",-1));
                ToastUtils.showShort("下载失败");
            }
    
            @Override
            public void onPaused() {
                downloadTask = null;
                ToastUtils.showShort("下载暂停");
            }
    
            @Override
            public void onCanceled() {
                downloadTask = null;
                stopForeground(true);
                ToastUtils.showShort("下载取消");
            }
        };
    
        public DownloadService() {
        }
        
        private DownloadBinder mBinder = new DownloadBinder();
    
        class DownloadBinder extends Binder{
            /**
             * 开始下载
             * @param url
             */
            public void startDownload(String url){
                if (downloadTask == null){
                    downloadUrl = url;
                    downloadTask = new DownloadTask(listener);
                    downloadTask.execute(downloadUrl);
                    startForeground(1,getNotification("Downloading ...",0));
                    ToastUtils.showShort("Downloading ...");
                }
            }
    
            /**
             * 暂停下载
             */
            public void pauseDownload(){
                if (downloadTask != null){
                    downloadTask.pauseDownload();;
                }
            }
    
            /**
             * 取消下载
             */
            public void cancelDownload(){
                if (downloadTask != null){
                    downloadTask.cancelDownload();
                }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();
                        }
    
                    }
                }
            }
        }
    
        @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,DownloadActivity.class);
            PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            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();
        }
    }
    

    现在下载的服务已实现,后端的工作基本完成,接下来就开始编写前端的部分。在布局中放置3个按钮,分别用于开始下载、暂停下载、取消下载,如下:

    public class DownloadActivity extends AppCompatActivity implements View.OnClickListener {
        
        private DownloadService.DownloadBinder downloadBinder;
        
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                downloadBinder = (DownloadService.DownloadBinder) service;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_download);
    
            Button start_download = (Button) findViewById(R.id.start_download);
            Button pause_download = (Button) findViewById(R.id.pause_download);
            Button cancel_download = (Button) findViewById(R.id.cancel_download);
            start_download.setOnClickListener(this);
            pause_download.setOnClickListener(this);
            cancel_download.setOnClickListener(this);
    
            Intent intent = new Intent(this,DownloadService.class);
            startService(intent);//启动服务
            bindService(intent,connection,BIND_AUTO_CREATE);//绑定服务
            if (ContextCompat.checkSelfPermission(DownloadActivity.this, android.Manifest.
                    permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(DownloadActivity.this,new 
                        String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
            }
        }
    
        @Override
        public void onClick(View v) {
            if (downloadBinder == null){
                return;
            }
            switch (v.getId()){
                // 开始下载
                case R.id.start_download:
                    String url = "https://raw.githubusercontent.com/guolindev/eclipse/" +
                            "master/eclipse-inst-win64.exe";
                    downloadBinder.startDownload(url);
                    break;
                
                // 暂停下载
                case R.id.pause_download:
                    downloadBinder.pauseDownload();
                    break;
                
                // 取消下载
                case R.id.cancel_download:
                    downloadBinder.cancelDownload();
                    break;
                
                default:
                    break;
            }
        }
    
        @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){
                        ToastUtils.showShort("拒绝权限将无法使用程序");
                        finish();
                    }
                    break;
                default:
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(connection);//解绑服务
        }
    }
    

    最后,别忘了声明权限:

     <!-- 访问SD卡权限 -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <!-- 访问网络权限 -->
     <uses-permission android:name="android.permission.INTERNET" />
    

    以上,就是整个的下载示例。

    本篇文章就介绍到这。

    相关文章

      网友评论

      本文标题:第一行代码读书笔记 10-- 探究服务(下)

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