您的位置:首页 > 理论基础 > 计算机网络

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)文件的上传

文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
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就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: