Android使用OKHttp3实现下载(断点续传、显示进度)

来源:互联网 发布:重庆邮电大学就业知乎 编辑:程序博客网 时间:2024/06/11 21:14
  1. 转载自 http://blog.csdn.net/cfy137000/article/details/54838608  侵权删


  2. 在项目的build文件中配置java1.8
  3. defaultConfig {  
  4.         applicationId "com.lanou3g.downdemo"  
  5.         minSdkVersion 15  
  6.         targetSdkVersion 24  
  7.         versionCode 1  
  8.         versionName "1.0"  
  9.         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"  
  10.         //为了开启Java8  
  11.         jackOptions{  
  12.             enabled true;  

  1. //开启Java1.8 能够使用lambda表达式  
  2.     compileOptions{  
  3.         sourceCompatibility JavaVersion.VERSION_1_8  
  4.         targetCompatibility JavaVersion.VERSION_1_8  
  5.     }  


  1.     //OKHttp  
  2.     compile 'com.squareup.okhttp3:okhttp:3.6.0'  
  3.     //RxJava和RxAndroid 用来做线程切换的  
  4.     compile 'io.reactivex.rxjava2:rxandroid:2.0.1'  
  5.     compile 'io.reactivex.rxjava2:rxjava:2.0.1'  

这是接下来的布局文件
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/activity_main"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:paddingBottom="@dimen/activity_vertical_margin"  
  8.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  9.     android:paddingRight="@dimen/activity_horizontal_margin"  
  10.     android:paddingTop="@dimen/activity_vertical_margin"  
  11.     android:orientation="vertical"  
  12.     tools:context="com.lanou3g.downdemo.MainActivity">  
  13.   
  14.     <LinearLayout  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:orientation="horizontal">  
  18.         <ProgressBar  
  19.             android:id="@+id/main_progress1"  
  20.             android:layout_width="0dp"  
  21.             android:layout_weight="1"  
  22.             android:layout_height="match_parent"  
  23.             style="@style/Widget.AppCompat.ProgressBar.Horizontal" />  
  24.         <Button  
  25.             android:id="@+id/main_btn_down1"  
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content"  
  28.             android:text="下载1"/>  
  29.         <Button  
  30.             android:id="@+id/main_btn_cancel1"  
  31.             android:layout_width="wrap_content"  
  32.             android:layout_height="wrap_content"  
  33.             android:text="取消1"/>  
  34.     </LinearLayout>  
  35.     <LinearLayout  
  36.         android:layout_width="match_parent"  
  37.         android:layout_height="wrap_content"  
  38.         android:orientation="horizontal">  
  39.         <ProgressBar  
  40.             android:id="@+id/main_progress2"  
  41.             android:layout_width="0dp"  
  42.             android:layout_weight="1"  
  43.             android:layout_height="match_parent"  
  44.             style="@style/Widget.AppCompat.ProgressBar.Horizontal" />  
  45.         <Button  
  46.             android:id="@+id/main_btn_down2"  
  47.             android:layout_width="wrap_content"  
  48.             android:layout_height="wrap_content"  
  49.             android:text="下载2"/>  
  50.         <Button  
  51.             android:id="@+id/main_btn_cancel2"  
  52.             android:layout_width="wrap_content"  
  53.             android:layout_height="wrap_content"  
  54.             android:text="取消2"/>  
  55.     </LinearLayout>  
  56.     <LinearLayout  
  57.         android:layout_width="match_parent"  
  58.         android:layout_height="wrap_content"  
  59.         android:orientation="horizontal">  
  60.         <ProgressBar  
  61.             android:id="@+id/main_progress3"  
  62.             android:layout_width="0dp"  
  63.             android:layout_weight="1"  
  64.             android:layout_height="match_parent"  
  65.             style="@style/Widget.AppCompat.ProgressBar.Horizontal" />  
  66.         <Button  
  67.             android:id="@+id/main_btn_down3"  
  68.             android:layout_width="wrap_content"  
  69.             android:layout_height="wrap_content"  
  70.             android:text="下载3"/>  
  71.         <Button  
  72.             android:id="@+id/main_btn_cancel3"  
  73.             android:layout_width="wrap_content"  
  74.             android:layout_height="wrap_content"  
  75.             android:text="取消3"/>  
  76.     </LinearLayout>  
  77. </LinearLayout>  

创建一个类继承application
  1. public class MyApp extends Application {  
  2.     public static Context sContext;//全局的Context对象  
  3.   
  4.     @Override  
  5.     public void onCreate() {  
  6.         super.onCreate();  
  7.         sContext = this;  
  8.     }  
  9. }  
需要在清单文件中进行初始化,并且添加一个网络权限


下载管理器 
  1. public class DownloadManager {  
  2.   
  3.     private static final AtomicReference<DownloadManager> INSTANCE = new AtomicReference<>();  
  4.     private HashMap<String, Call> downCalls;//用来存放各个下载的请求  
  5.     private OkHttpClient mClient;//OKHttpClient;  
  6.   
  7.     //获得一个单例类  
  8.     public static DownloadManager getInstance() {  
  9.         for (; ; ) {  
  10.             DownloadManager current = INSTANCE.get();  
  11.             if (current != null) {  
  12.                 return current;  
  13.             }  
  14.             current = new DownloadManager();  
  15.             if (INSTANCE.compareAndSet(null, current)) {  
  16.                 return current;  
  17.             }  
  18.         }  
  19.     }  
  20.   
  21.     private DownloadManager() {  
  22.         downCalls = new HashMap<>();  
  23.         mClient = new OkHttpClient.Builder().build();  
  24.     }  
  25.   
  26.     /** 
  27.      * 开始下载 
  28.      * 
  29.      * @param url              下载请求的网址 
  30.      * @param downLoadObserver 用来回调的接口 
  31.      */  
  32.     public void download(String url, DownLoadObserver downLoadObserver) {  
  33.         Observable.just(url)  
  34.                 .filter(s -> !downCalls.containsKey(s))//call的map已经有了,就证明正在下载,则这次不下载  
  35.                 .flatMap(s -> Observable.just(createDownInfo(s)))  
  36.                 .map(this::getRealFileName)//检测本地文件夹,生成新的文件名  
  37.                 .flatMap(downloadInfo -> Observable.create(new DownloadSubscribe(downloadInfo)))//下载  
  38.                 .observeOn(AndroidSchedulers.mainThread())//在主线程回调  
  39.                 .subscribeOn(Schedulers.io())//在子线程执行  
  40.                 .subscribe(downLoadObserver);//添加观察者  
  41.   
  42.     }  
  43.   
  44.     public void cancel(String url) {  
  45.         Call call = downCalls.get(url);  
  46.         if (call != null) {  
  47.             call.cancel();//取消  
  48.         }  
  49.         downCalls.remove(url);  
  50.     }  
  51.   
  52.     /** 
  53.      * 创建DownInfo 
  54.      * 
  55.      * @param url 请求网址 
  56.      * @return DownInfo 
  57.      */  
  58.     private DownloadInfo createDownInfo(String url) {  
  59.         DownloadInfo downloadInfo = new DownloadInfo(url);  
  60.         long contentLength = getContentLength(url);//获得文件大小  
  61.         downloadInfo.setTotal(contentLength);  
  62.         String fileName = url.substring(url.lastIndexOf("/"));  
  63.         downloadInfo.setFileName(fileName);  
  64.         return downloadInfo;  
  65.     }  
  66.   
  67.     private DownloadInfo getRealFileName(DownloadInfo downloadInfo) {  
  68.         String fileName = downloadInfo.getFileName();  
  69.         long downloadLength = 0, contentLength = downloadInfo.getTotal();  
  70.         File file = new File(MyApp.sContext.getFilesDir(), fileName);  
  71.         if (file.exists()) {  
  72.             //找到了文件,代表已经下载过,则获取其长度  
  73.             downloadLength = file.length();  
  74.         }  
  75.         //之前下载过,需要重新来一个文件  
  76.         int i = 1;  
  77.         while (downloadLength >= contentLength) {  
  78.             int dotIndex = fileName.lastIndexOf(".");  
  79.             String fileNameOther;  
  80.             if (dotIndex == -1) {  
  81.                 fileNameOther = fileName + "(" + i + ")";  
  82.             } else {  
  83.                 fileNameOther = fileName.substring(0, dotIndex)  
  84.                         + "(" + i + ")" + fileName.substring(dotIndex);  
  85.             }  
  86.             File newFile = new File(MyApp.sContext.getFilesDir(), fileNameOther);  
  87.             file = newFile;  
  88.             downloadLength = newFile.length();  
  89.             i++;  
  90.         }  
  91.         //设置改变过的文件名/大小  
  92.         downloadInfo.setProgress(downloadLength);  
  93.         downloadInfo.setFileName(file.getName());  
  94.         return downloadInfo;  
  95.     }  
  96.   
  97.     private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {  
  98.         private DownloadInfo downloadInfo;  
  99.   
  100.         public DownloadSubscribe(DownloadInfo downloadInfo) {  
  101.             this.downloadInfo = downloadInfo;  
  102.         }  
  103.   
  104.         @Override  
  105.         public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {  
  106.             String url = downloadInfo.getUrl();  
  107.             long downloadLength = downloadInfo.getProgress();//已经下载好的长度  
  108.             long contentLength = downloadInfo.getTotal();//文件的总长度  
  109.             //初始进度信息  
  110.             e.onNext(downloadInfo);  
  111.   
  112.             Request request = new Request.Builder()  
  113.                     //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分  
  114.                     .addHeader("RANGE""bytes=" + downloadLength + "-" + contentLength)  
  115.                     .url(url)  
  116.                     .build();  
  117.             Call call = mClient.newCall(request);  
  118.             downCalls.put(url, call);//把这个添加到call里,方便取消  
  119.             Response response = call.execute();  
  120.   
  121.             File file = new File(MyApp.sContext.getFilesDir(), downloadInfo.getFileName());  
  122.             InputStream is = null;  
  123.             FileOutputStream fileOutputStream = null;  
  124.             try {  
  125.                 is = response.body().byteStream();  
  126.                 fileOutputStream = new FileOutputStream(file, true);  
  127.                 byte[] buffer = new byte[2048];//缓冲数组2kB  
  128.                 int len;  
  129.                 while ((len = is.read(buffer)) != -1) {  
  130.                     fileOutputStream.write(buffer, 0, len);  
  131.                     downloadLength += len;  
  132.                     downloadInfo.setProgress(downloadLength);  
  133.                     e.onNext(downloadInfo);  
  134.                 }  
  135.                 fileOutputStream.flush();  
  136.                 downCalls.remove(url);  
  137.             } finally {  
  138.                 //关闭IO流  
  139.                 IOUtil.closeAll(is, fileOutputStream);  
  140.   
  141.             }  
  142.             e.onComplete();//完成  
  143.         }  
  144.     }  
  145.   
  146.     /** 
  147.      * 获取下载长度 
  148.      * 
  149.      * @param downloadUrl 
  150.      * @return 
  151.      */  
  152.     private long getContentLength(String downloadUrl) {  
  153.         Request request = new Request.Builder()  
  154.                 .url(downloadUrl)  
  155.                 .build();  
  156.         try {  
  157.             Response response = mClient.newCall(request).execute();  
  158.             if (response != null && response.isSuccessful()) {  
  159.                 long contentLength = response.body().contentLength();  
  160.                 response.close();  
  161.                 return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;  
  162.             }  
  163.         } catch (IOException e) {  
  164.             e.printStackTrace();  
  165.         }  
  166.         return DownloadInfo.TOTAL_ERROR;  
  167.     }  
  168.   
  169.   
  170. }  

观察者
  1. public  abstract class DownLoadObserver implements Observer<DownloadInfo> {  
  2.     protected Disposable d;//可以用于取消注册的监听者  
  3.     protected DownloadInfo downloadInfo;  
  4.     @Override  
  5.     public void onSubscribe(Disposable d) {  
  6.         this.d = d;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void onNext(DownloadInfo downloadInfo) {  
  11.         this.downloadInfo = downloadInfo;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void onError(Throwable e) {  
  16.         e.printStackTrace();  
  17.     }  
  18.   
  19.   
  20. }  

下载的注册信息
  1. public class DownloadInfo {  
  2.     public static final long TOTAL_ERROR = -1;//获取进度失败  
  3.     private String url;  
  4.     private long total;  
  5.     private long progress;  
  6.     private String fileName;  
  7.       
  8.     public DownloadInfo(String url) {  
  9.         this.url = url;  
  10.     }  
  11.   
  12.     public String getUrl() {  
  13.         return url;  
  14.     }  
  15.   
  16.     public String getFileName() {  
  17.         return fileName;  
  18.     }  
  19.   
  20.     public void setFileName(String fileName) {  
  21.         this.fileName = fileName;  
  22.     }  
  23.   
  24.     public long getTotal() {  
  25.         return total;  
  26.     }  
  27.   
  28.     public void setTotal(long total) {  
  29.         this.total = total;  
  30.     }  
  31.   
  32.     public long getProgress() {  
  33.         return progress;  
  34.     }  
  35.   
  36.     public void setProgress(long progress) {  
  37.         this.progress = progress;  
  38.     }  
  39. }  


判断是否为空  并且进行关闭
  1. public class IOUtil {  
  2.     public static void closeAll(Closeable... closeables){  
  3.         if(closeables == null){  
  4.             return;  
  5.         }  
  6.         for (Closeable closeable : closeables) {  
  7.             if(closeable!=null){  
  8.                 try {  
  9.                     closeable.close();  
  10.                 } catch (IOException e) {  
  11.                     e.printStackTrace();  
  12.                 }  
  13.             }  
  14.         }  
  15.     }  

  1. public class MainActivity extends AppCompatActivity implements View.OnClickListener {  
  2.     private Button downloadBtn1, downloadBtn2, downloadBtn3;  
  3.     private Button cancelBtn1, cancelBtn2, cancelBtn3;  
  4.     private ProgressBar progress1, progress2, progress3;  
  5.     private String url1 = "http://192.168.31.169:8080/out/dream.flac";  
  6.     private String url2 = "http://192.168.31.169:8080/out/music.mp3";  
  7.     private String url3 = "http://192.168.31.169:8080/out/code.zip";  
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.   
  13.         downloadBtn1 = bindView(R.id.main_btn_down1);  
  14.         downloadBtn2 = bindView(R.id.main_btn_down2);  
  15.         downloadBtn3 = bindView(R.id.main_btn_down3);  
  16.   
  17.         cancelBtn1 = bindView(R.id.main_btn_cancel1);  
  18.         cancelBtn2 = bindView(R.id.main_btn_cancel2);  
  19.         cancelBtn3 = bindView(R.id.main_btn_cancel3);  
  20.   
  21.         progress1 = bindView(R.id.main_progress1);  
  22.         progress2 = bindView(R.id.main_progress2);  
  23.         progress3 = bindView(R.id.main_progress3);  
  24.   
  25.         downloadBtn1.setOnClickListener(this);  
  26.         downloadBtn2.setOnClickListener(this);  
  27.         downloadBtn3.setOnClickListener(this);  
  28.   
  29.         cancelBtn1.setOnClickListener(this);  
  30.         cancelBtn2.setOnClickListener(this);  
  31.         cancelBtn3.setOnClickListener(this);  
  32.     }  
  33.   
  34.     @Override  
  35.     public void onClick(View v) {  
  36.         switch (v.getId()) {  
  37.             case R.id.main_btn_down1:  
  38.                 DownloadManager.getInstance().download(url1, new DownLoadObserver() {  
  39.                     @Override  
  40.                     public void onNext(DownloadInfo value) {  
  41.                         super.onNext(value);  
  42.                         progress1.setMax((int) value.getTotal());  
  43.                         progress1.setProgress((int) value.getProgress());  
  44.                     }  
  45.   
  46.                     @Override  
  47.                     public void onComplete() {  
  48.                         if(downloadInfo != null){  
  49.                             Toast.makeText(MainActivity.this,  
  50.                                     downloadInfo.getFileName() + "-DownloadComplete",  
  51.                                     Toast.LENGTH_SHORT).show();  
  52.                         }  
  53.                     }  
  54.                 });  
  55.                 break;  
  56.             case R.id.main_btn_down2:  
  57.                 DownloadManager.getInstance().download(url2, new DownLoadObserver() {  
  58.                     @Override  
  59.                     public void onNext(DownloadInfo value) {  
  60.                         super.onNext(value);  
  61.                         progress2.setMax((int) value.getTotal());  
  62.                         progress2.setProgress((int) value.getProgress());  
  63.                     }  
  64.   
  65.                     @Override  
  66.                     public void onComplete() {  
  67.                         if(downloadInfo != null){  
  68.                             Toast.makeText(MainActivity.this,  
  69.                                     downloadInfo.getFileName() + Uri.encode("下载完成"),  
  70.                                     Toast.LENGTH_SHORT).show();  
  71.                         }  
  72.                     }  
  73.                 });  
  74.                 break;  
  75.             case R.id.main_btn_down3:  
  76.                 DownloadManager.getInstance().download(url3, new DownLoadObserver() {  
  77.                     @Override  
  78.                     public void onNext(DownloadInfo value) {  
  79.                         super.onNext(value);  
  80.                         progress3.setMax((int) value.getTotal());  
  81.                         progress3.setProgress((int) value.getProgress());  
  82.                     }  
  83.   
  84.                     @Override  
  85.                     public void onComplete() {  
  86.                         if(downloadInfo != null){  
  87.                             Toast.makeText(MainActivity.this,  
  88.                                     downloadInfo.getFileName() + "下载完成",  
  89.                                     Toast.LENGTH_SHORT).show();  
  90.                         }  
  91.                     }  
  92.                 });  
  93.                 break;  
  94.             case R.id.main_btn_cancel1:  
  95.                 DownloadManager.getInstance().cancel(url1);  
  96.                 break;  
  97.             case R.id.main_btn_cancel2:  
  98.                 DownloadManager.getInstance().cancel(url2);  
  99.                 break;  
  100.             case R.id.main_btn_cancel3:  
  101.                 DownloadManager.getInstance().cancel(url3);  
  102.                 break;  
  103.         }  
  104.     }  
  105.       
  106.     private <T extends View> T bindView(@IdRes int id){  
  107.         View viewById = findViewById(id);  
  108.         return (T) viewById;  
  109.     }  
  110. }  


阅读全文
0 1
原创粉丝点击