神刀安全网

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


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

  • 服务的实践应用。
第一行代码读书笔记 10-- 探究服务(下)

图片来源于网络

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

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

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

下载示例效果

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

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– 探究服务(下)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址