OKHTTP网络请求——上传下载
2016-07-22 17:39
357 查看
http://www.open-open.com/lib/view/open1462258981866.html
http://www.2cto.com/kf/201604/500820.html
概念:okHttp 是同步请求。
noHttp 是异步请求
下面上代码一一说明:
要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:
?
这样就将OkHttp导入到项目中了。
(1)GET请求
最简单的GET请求用法如下:
?
如果请求中要添加Header头和参数,可以用下面的方式:
?
需要注意的是,上面的代码中,callback是请求后的回调接口,代码如下:
?
这个回调接口需要注意的是,onResponse和onFailure都不是在UI线程中执行的,所以如果我们要在onResponse或onFailure中进行UI相关的操作,需要在UI线程中进行。
(2)POST请求
比较简单的POST请求,用法如下:
?
这里我们需要先构造一个RequestBody,然后把需要携带的参数放到RequestBody中,然后使用这个RequestBody构建一个Request请求,最后将这个请求放入队列中执行
如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:
?
上面的代码中,tempFile是一个文本文件,为了POST提交文件和一些其他的参数,我们使用MultipartBody来构建一个请求体,需要注意的是,因为POST的内容含有文件,所以我们必须为这个请求体设置setType(MultipartBody.FORM)
(3)文件的上传
文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
上面需要注意的是,上传文件需要实现自定义的RequestBody,也就是上面的ProgressRequestBody,在ProgressRequestBody中获取上传的进度。
(4)文件的下载
下载和上传类似,区别在于,需要我们实习自定义的ResponseBody而不是RequestBody了,下面上代码:
如果我们在项目中直接使用上面的代码来进行http请求的话,势必会比较麻烦,所以这里我们需要封装上面的代码,尽量在项目中能用简短的代码完成网络请求。另外,一个项目中肯定会有很多个网络请求,我们没必要在每次网络请求中都创建一个OkHttpClient对象,所有的请求公用一个OkHttpClient就可以了。
http://www.2cto.com/kf/201604/500820.html
概念:okHttp 是同步请求。
noHttp 是异步请求
下面上代码一一说明:
要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:
?
(1)GET请求
最简单的GET请求用法如下:
?
?
?
(2)POST请求
比较简单的POST请求,用法如下:
?
如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:
?
(3)文件的上传
文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
package com.test.testokhttp; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Buffer; import okio.BufferedSink; import okio.BufferedSource; import okio.ForwardingSink; import okio.ForwardingSource; import okio.Okio; import okio.Sink; import okio.Source; /** 使用okHttp 上传文件并显示进度 */ public class UploadActivity extends AppCompatActivity { private OkHttpClient okHttpClient; private TextView resultTextView; private ProgressBar progressBar; private File tempFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_upload); setTitle("上传文件并显示进度"); resultTextView = (TextView) findViewById(R.id.result_textview); progressBar = (ProgressBar) findViewById(R.id.progress_bar); progressBar.setMax(100); okHttpClient = new OkHttpClient.Builder() .readTimeout(30, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .build(); } //点击按钮开始上传文件 public void startUploadClick(View view) { tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.pdf"); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", "test.pdf", RequestBody.create(MediaType.parse("application/pdf; charset=utf-8"), tempFile)) .build(); ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, progressListener);//通过对RequestBody的包装,实现对上传进度的监听(携带了一个接口的引用) Request request = new Request.Builder() .url("http://192.168.1.170:8088/okhttp/test_upload_file.php") .post(progressRequestBody) .build(); okHttpClient.newCall(request).enqueue(callback); } //通过实现进度回调接口中的方法,来显示进度. 通过回调机制实现了线程间的通讯。 //原理是,将回调类定义为一个实现某种接口的类(接口可以省掉),然后在每个多线程类上都注入一个回调对象。当线程执行完毕后,通过回调对象执行自己的回调方法,从而达到线程通信的目的。 //白话(我把我的引用给你,你干完了某事,调用我的方法) private ProgressListener progressListener = new ProgressListener() { @Override public void update(long bytesRead, long contentLength, boolean done) { int progress = (int) (100.0 * bytesRead / contentLength); progressBar.setProgress(progress); } }; //请求后的回调方法 private Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { setResult(e.getMessage(), false); } @Override public void onResponse(Call call, Response response) throws IOException { setResult(response.body().string(), true); } }; //显示请求返回的结果(在主线程) private void setResult(final String msg, final boolean success) { runOnUiThread(new Runnable() { @Override public void run() { if (success) { Toast.makeText(UploadActivity.this, "请求成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(UploadActivity.this, "请求失败", Toast.LENGTH_SHORT).show(); } resultTextView.setText(msg); } }); } //自定义的RequestBody,能够显示进度 public class ProgressRequestBody extends RequestBody { //实际的待包装请求体 private final RequestBody requestBody; //进度回调接口 private final ProgressListener progressListener; //包装完成的BufferedSink private BufferedSink bufferedSink; /** * 构造函数,赋值 * * @param requestBody 待包装的请求体 * @param progressListener 回调接口 */ public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) { this.requestBody = requestBody; this.progressListener = progressListener; } /** * 重写调用实际的响应体的contentType * * @return MediaType */ @Override public MediaType contentType() { return requestBody.contentType(); } /** * 重写调用实际的响应体的contentLength * * @return contentLength * @throws IOException 异常 */ @Override public long contentLength() throws IOException { return requestBody.contentLength(); } /** * 重写进行写入 * * @param sink BufferedSink * @throws IOException 异常 */ @Override public void writeTo(BufferedSink sink) throws IOException { if (bufferedSink == null) { //包装 bufferedSink = Okio.buffer(sink(sink)); } //写入 requestBody.writeTo(bufferedSink); //必须调用flush,否则最后一部分数据可能不会被写入 bufferedSink.flush(); } /** * 写入,回调进度接口 * * @param sink Sink * @return Sink */ private Sink sink(Sink sink) { return new ForwardingSink(sink) { //当前写入字节数 long bytesWritten = 0L; //总字节长度,避免多次调用contentLength()方法 long contentLength = 0L; @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); if (contentLength == 0) { //获得contentLength的值,后续不再调用 contentLength = contentLength(); } //增加当前写入的字节数 bytesWritten += byteCount; //回调(在子线程中回调,在主线程中修改ui) progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength); } }; } } //进度回调接口 interface ProgressListener { void update(long bytesRead, long contentLength, boolean done); } }
上面需要注意的是,上传文件需要实现自定义的RequestBody,也就是上面的ProgressRequestBody,在ProgressRequestBody中获取上传的进度。
(4)文件的下载
下载和上传类似,区别在于,需要我们实习自定义的ResponseBody而不是RequestBody了,下面上代码:
import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; import okio.Okio; import okio.Source; public class DownloadActivity extends AppCompatActivity { private OkHttpClient okHttpClient; private TextView resultTextView; private ProgressBar progressBar; private File tempFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_download); setTitle("下载文件并显示进度"); okHttpClient = new OkHttpClient.Builder() .addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ProgressResponseBody(originalResponse.body(), progressListener)) .build(); } }) .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(300, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build(); resultTextView = (TextView) findViewById(R.id.result_textview); progressBar = (ProgressBar) findViewById(R.id.progress_bar); tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".pdf"); } //下载文件 public void startDownloadClick(View view) { Request request = new Request.Builder() .url("http://192.168.1.170:8088/okhttp/test.pdf") .build(); okHttpClient.newCall(request).enqueue(callback); } private ProgressListener progressListener = new ProgressListener() { @Override public void update(long bytesRead, long contentLength, boolean done) { int progress = (int) (100.0 * bytesRead / contentLength); progressBar.setProgress(progress); } }; //请求后的回调方法 private Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { setResult(e.getMessage(), false); } @Override public void onResponse(Call call, Response response) throws IOException { if(response != null) { //下载完成,保存数据到文件 InputStream is = response.body().byteStream(); FileOutputStream fos = new FileOutputStream(tempFile); byte[] buf = new byte[1024]; int hasRead = 0; while((hasRead = is.read(buf)) > 0) { fos.write(buf, 0, hasRead); } fos.close(); is.close(); setResult("下载成功", true); } } }; //显示请求返回的结果 private void setResult(final String msg, final boolean success) { runOnUiThread(new Runnable() { @Override public void run() { if (success) { Toast.makeText(DownloadActivity.this, "请求成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(DownloadActivity.this, "请求失败", Toast.LENGTH_SHORT).show(); } resultTextView.setText(msg); } }); } //自定义的ResponseBody,在其中处理进度 private static class ProgressResponseBody extends ResponseBody { private final ResponseBody responseBody; private final ProgressListener progressListener; private BufferedSource bufferedSource; public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) { this.responseBody = responseBody; this.progressListener = progressListener; } @Override public MediaType contentType() { return responseBody.contentType(); } @Override public long contentLength() { return responseBody.contentLength(); } @Override public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } return bufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); // read() returns the number of bytes read, or -1 if this source is exhausted. totalBytesRead += bytesRead != -1 ? bytesRead : 0; progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); return bytesRead; } }; } } //进度回调接口 interface ProgressListener { void update(long bytesRead, long contentLength, boolean done); } }
如果我们在项目中直接使用上面的代码来进行http请求的话,势必会比较麻烦,所以这里我们需要封装上面的代码,尽量在项目中能用简短的代码完成网络请求。另外,一个项目中肯定会有很多个网络请求,我们没必要在每次网络请求中都创建一个OkHttpClient对象,所有的请求公用一个OkHttpClient就可以了。
相关文章推荐
- TCP网络编程
- 神经网络:表示
- UDP网络编程
- Network——Socket网络通信机制以及实现举例(TCP、UDP等)
- Java实现——Socket网络通信的机制以及实现举例
- Node.js HTTP模块与事件模块
- AngularJS中$http服务的简单用法
- 直播推流端网络优化策略
- 【Android进阶】ListView使用“内存双缓存+硬盘缓存”加载网络图片
- tcp协议实现C/S通信(代码)
- iis https 客户端证书
- http://www.cnblogs.com/EricaMIN1987_IT/p/3837436.html
- webservice接口实现数据共享的实现的初步解决方案(更新、删除)
- http://www.hulian.top/zixun/post/4771.html
- 如何加强神经网络训练
- IP地址的分类——a,b,c 类是如何划分的
- iOS网络HTTP、TCP、UDP、Socket 知识总结
- Android如何使用Https
- TCP粘包,拆包及解决方法
- Android学习笔记035之HTTP协议