第一步:创建一个处理下载结果的接口
public interface DownloadListener {// 更新进度条 void onPRegress(int progress);// 下载成功 void onSuccess();// 下载失败 void onFaild();// 暂停下载 void onPaused();// 取消下载 void onCanceled();}第二步:创建下载的异步任务,引用了okhttp
/** * AsyncTask三个参数 * 参数1:在执行AsyncTask时需要传入的参数。可用于后台任务中 * 参数2.后台任务执行时,如需显示进度,则使用这里指定的泛型作为进度单位 * 参数3.但任务执行完毕后,如果需要返回结果,则这里指定的泛型作为返回值类型 */public class DownLoadTask extends AsyncTask<String, Integer, Integer> { public static final int TYPE_SUCCESS = 0; public static final int TYPE_FAIL = 2; public static final int TYPE_PAUSED = 3; public static final int TYPE_CANCLE = 4; private DownloadListener listener; private boolean isCanceled = false; private boolean isPaused = false; private int lastProgress; public DownLoadTask(DownloadListener downloadListener) { listener = downloadListener; } /** * 在后台任务执行之前调用,用于进行界面上的初始化操作,比如显示一个进度对话框 */ @Override protected void onPreExecute() { super.onPreExecute(); } /** * 在这里处理所有的耗时任务,任务一旦完成通过return返回 * 这里不能对UI进行操作,因为此处代码都运行在子线程中。 * * @param params * @return */ @Override protected Integer doInBackground(String... params) { InputStream inputStream = null; RandomaccessFile saveFile = null; File file = null; try {// 记录已下载文件的长度 long downLoadLenght = 0; String downloadUrl = params[0]; String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));// SD卡的download目录 String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); file = new File(directory + fileName); if (file.exists()) { downLoadLenght = file.length(); }// 获取文件的总长度 long contentLenght = getContentLenght(downloadUrl); if (contentLenght == 0) { return TYPE_FAIL; } else if (contentLenght == downLoadLenght) {// 已下载的字节和文件总字节相等,说明下载已经完成 return TYPE_SUCCESS; } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder()// 断点下载,指定从哪一个节点开始下载 .addHeader("RANGE", "bytes=" + downLoadLenght + "-") .url(downloadUrl) .build(); Response response = client.newCall(request).execute(); if (response != null) { inputStream = response.body().byteStream(); saveFile = new RandomAccessFile(file, "rw"); saveFile.seek(downLoadLenght);//跳过已下载的字节 } byte[] b = new byte[1024]; int total = 0; int len; while ((len = inputStream.read(b)) != -1) { if (isCanceled) { return TYPE_CANCLE; } else if (isPaused) { return TYPE_PAUSED; } else { total += len; saveFile.write(b, 0, len); int progress = (int) ((total + downLoadLenght) * 100 / contentLenght);// 通知进度条更新 publishProgress(progress); } } response.body().close(); return TYPE_SUCCESS; } catch (Exception e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (saveFile != null) { saveFile.close(); } if (isCanceled && file != null) { file.delete(); } } catch (Exception e) { } } return TYPE_FAIL; } /** * 获取文件的总长度 * @param downloadUrl * @return * @throws IOException */ private long getContentLenght(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 contentLenght = response.body().contentLength(); response.close(); return contentLenght; } return 0; } /** * 这里可以更新UI了 * * @param values */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values);// 从参数中获取到当前的下载进度,和上一次下载进度对比,有变化则通知进度条更新 int progress = values[0]; if (progress > lastProgress) { listener.onPregress(progress); lastProgress = progress; } } /** * 但后台任务完成后return后这个方法马上会执行 * 并且rentur的值可以在参数中获得。这里做收尾工作 */ @Override protected void onPostExecute(Integer integer) { switch (integer) { case TYPE_CANCLE: listener.onCanceled(); break; case TYPE_SUCCESS: listener.onSuccess(); break; case TYPE_PAUSED: listener.onPaused(); break; case TYPE_FAIL: listener.onFaild(); break; } } public void pauseDownload(){ isPaused =true; } public void cancleDownload(){ isCanceled =true; } /** * 执行这个异步任务通过这个异步类的对象.execute() */ }第三步:创建一个服务,并通过通知通知下载进度
//保证DownLoadTask可以一直在后台运行,我们需要创建一个下载的服务//通过new -> service 创建一个service 这样就可以自动去清单文件注册了public class DownloadService extends Service { private DownLoadTask downLoadTask; private String downloadUrl; private DownloadListener downloadListener = new DownloadListener() { @Override public void onPregress(int progress) { getNotificationManager().notify(1, getNotification("Dowing...", 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 onFaild() { downLoadTask = null;// 下载失败时通知将前台服务关闭,并创建一个下载失败的通知 stopForeground(true); getNotificationManager().notify(1, getNotification("Download Fail", -1)); Toast.makeText(DownloadService.this, "Download Fail", 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 Cancle", Toast.LENGTH_SHORT).show(); } }; private DownloadBinder binder = new DownloadBinder(); /** * */ 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, "Downing...", Toast.LENGTH_SHORT); } } public void pauseDownload() { if (downLoadTask != null) { downLoadTask.pauseDownload(); } } public void cancelDownload() { if (downLoadTask != null) { downLoadTask.cancleDownload(); } 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, "Cancled", Toast.LENGTH_SHORT).show(); } } } } private NotificationManager getNotificationManager() { return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } /** * 用于显示下载进度的通知 * @param title * @param progress * @return */ private Notification getNotification(String title, int progress) { Intent intent = new Intent(this, DownloadActivity.class); PendingIntent pendingIntent = 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.a8)); builder.setContentTitle(title); builder.setContentIntent(pendingIntent); if (progress > 0) {// 当progress大于或等于0时才需要显示下载进度 builder.setContentText(progress + "%");// 参数1:通知的做大进度。 2.通知的当前进度。 3.是否使用模糊进度条 builder.setProgress(100, progress, false); } return builder.build(); } public DownloadService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service.// throw new UnsupportedOperationException("Not yet implemented"); return binder; }}第四步:使用
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); initView(); } private void initView() { findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.pause).setOnClickListener(this); findViewById(R.id.cancle).setOnClickListener(this); Intent intent = new Intent(this, DownloadService.class); startService(intent); //启动服务 bindService(intent,connection,BIND_AUTO_CREATE); //绑定服务 if (ContextCompat.checkSelfPermission(DownloadActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(DownloadActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); } } @Override public void onClick(View v) { if (downloadBinder == null){ return; } switch (v.getId()) { case R.id.start: String url = "http://218.201.198.108:8081/apk/oil1.apk"; downloadBinder.startDownload(url); break; case R.id.pause: downloadBinder.pauseDownload(); break; case R.id.cancle: downloadBinder.cancelDownload(); 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){ Toast.makeText(this,"权限被拒绝了呦",Toast.LENGTH_SHORT).show(); finish(); } break; } } @Override protected void onDestroy() { super.onDestroy();// 要记得解绑,不然可能会造成内存溢出 unbindService(connection); }}布局文件<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_download" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.lvyequeen.test.day06.DownloadActivity"> <Button android:text="start download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_marginLeft="23dp" android:layout_marginTop="31dp" android:id="@+id/start" /> <Button android:text="pause download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/start" android:layout_alignRight="@+id/start" android:layout_marginTop="36dp" android:id="@+id/pause" /> <Button android:text="cancle download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/pause" android:layout_alignLeft="@+id/pause" android:layout_marginTop="36dp" android:id="@+id/cancle" /></RelativeLayout>
新闻热点
疑难解答