要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:
1 dependencies { 2 compile fileTree(dir: 'libs', include: ['*.jar']) 3 testCompile 'junit:junit:4.12' 4 compile 'com.android.support:appcompat-v7:23.1.1' 5 compile 'com.squareup.okhttp3:okhttp:3.2.0' 6 }
这样就将OkHttp导入到项目中了。
(1)GET请求
最简单的GET请求用法如下:
1 //简单的Get请求,不带参数 2 public void simpleGetClick(View view) { 3 okHttpClient = new OkHttpClient(); 4 Request request = new Request.Builder() 5 .url("http://192.168.1.170:8088/okhttp/test_simple_get.php") 6 .build(); 7 okHttpClient.newCall(request).enqueue(callback); 8 }
如果请求中要添加Header头和参数,可以用下面的方式:
1 //带参数的Get请求 2 public void addParamGetClick(View view) { 3 okHttpClient = new OkHttpClient(); 4 Request request = new Request.Builder() 5 .addHeader("token", "asdlfjkasdljfaskdjfalsjkljalk") //请求头中加入参数 6 .url("http://192.168.1.170:8088/okhttp/test_param_get.php?username=zhangsan&phone=13888888888") //携带参数 7 .build(); 8 okHttpClient.newCall(request).enqueue(callback); 9 }
需要注意的是,上面的代码中,callback是请求后的回调接口,代码如下:
1 //请求后的回调接口 2 private Callback callback = new Callback() { 3 @Override 4 public void onFailure(Call call, IOException e) { 5 setResult(e.getMessage(), false); 6 } 7 8 @Override 9 public void onResponse(Call call, Response response) throws IOException { 10 setResult(response.body().string(), true); 11 } 12 };
这个回调接口需要注意的是,onResponse和onFailure都不是在UI线程中执行的,所以如果我们要在onResponse或onFailure中进行UI相关的操作,需要在UI线程中进行。
(2)POST请求
比较简单的POST请求,用法如下:
1 //简单的带参数和Header的post请求 2 public void simplePostClick(View view) { 3 okHttpClient = new OkHttpClient(); 4 RequestBody requestBody = new FormBody.Builder() 5 .add("username", "wangwu") 6 .add("password", "hello12345") 7 .add("gender", "female") 8 .build(); 9 Request request = new Request.Builder() 10 .url("http://192.168.1.170:8088/okhttp/test_simple_post.php") 11 .post(requestBody) 12 .addHeader("token", "helloworldhelloworldhelloworld") 13 .build(); 14 okHttpClient.newCall(request).enqueue(callback); 15 }
这里我们需要先构造一个RequestBody,然后把需要携带的参数放到RequestBody中,然后使用这个RequestBody构建一个Request请求,最后将这个请求放入队列中执行
如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:
1 //带文本参数和文件参数的post请求 2 public void filePostClick(View view) { 3 RequestBody fileBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), tempFile); 4 RequestBody requestBody = new MultipartBody.Builder() 5 .setType(MultipartBody.FORM) 6 .addFormDataPart("username", "wangwu") 7 .addFormDataPart("password", "hello12345") 8 .addFormDataPart("gender", "female") 9 .addFormDataPart("file", "info.txt", fileBody) 10 .build(); 11 Request request = new Request.Builder() 12 .url("http://192.168.1.170:8088/okhttp/test_param_post.php") 13 .post(requestBody) 14 .addHeader("token", "helloworldhelloworldhelloworld") 15 .build(); 16 okHttpClient.newCall(request).enqueue(callback); 17 }
上面的代码中,tempFile是一个文本文件,为了POST提交文件和一些其他的参数,我们使用MultipartBody来构建一个请求体,需要注意的是,因为POST的内容含有文件,所以我们必须为这个请求体设置setType(MultipartBody.FORM)
(3)文件的上传
文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
1 package com.test.testokhttp; 2 3 import android.os.Environment; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.widget.ProgressBar; 8 import android.widget.TextView; 9 import android.widget.Toast; 10 11 import java.io.File; 12 import java.io.IOException; 13 import java.util.concurrent.TimeUnit; 14 15 import okhttp3.Call; 16 import okhttp3.Callback; 17 import okhttp3.Interceptor; 18 import okhttp3.MediaType; 19 import okhttp3.MultipartBody; 20 import okhttp3.OkHttpClient; 21 import okhttp3.Request; 22 import okhttp3.RequestBody; 23 import okhttp3.Response; 24 import okhttp3.ResponseBody; 25 import okio.Buffer; 26 import okio.BufferedSink; 27 import okio.BufferedSource; 28 import okio.ForwardingSink; 29 import okio.ForwardingSource; 30 import okio.Okio; 31 import okio.Sink; 32 import okio.Source; 33 34 public class UploadActivity extends AppCompatActivity { 35 36 private OkHttpClient okHttpClient; 37 private TextView resultTextView; 38 private ProgressBar progressBar; 39 private File tempFile; 40 41 @Override 42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 setContentView(R.layout.activity_upload); 45 setTitle("上传文件并显示进度"); 46 47 resultTextView = (TextView) findViewById(R.id.result_textview); 48 progressBar = (ProgressBar) findViewById(R.id.progress_bar); 49 progressBar.setMax(100); 50 51 okHttpClient = new OkHttpClient.Builder() 52 .readTimeout(30, TimeUnit.SECONDS) 53 .connectTimeout(10, TimeUnit.SECONDS) 54 .writeTimeout(60, TimeUnit.SECONDS) 55 .build(); 56 } 57 58 //点击按钮开始上传文件 59 public void startUploadClick(View view) { 60 tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.pdf"); 61 RequestBody requestBody = new MultipartBody.Builder() 62 .setType(MultipartBody.FORM) 63 .addFormDataPart("file", "test.pdf", RequestBody.create(MediaType.parse("application/pdf; charset=utf-8"), tempFile)) 64 .build(); 65 ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, progressListener); 66 Request request = new Request.Builder() 67 .url("http://192.168.1.170:8088/okhttp/test_upload_file.php") 68 .post(progressRequestBody) 69 .build(); 70 okHttpClient.newCall(request).enqueue(callback); 71 } 72 73 //通过实现进度回调接口中的方法,来显示进度 74 private ProgressListener progressListener = new ProgressListener() { 75 @Override 76 public void update(long bytesRead, long contentLength, boolean done) { 77 int progress = (int) (100.0 * bytesRead / contentLength); 78 progressBar.setProgress(progress); 79 } 80 }; 81 82 //请求后的回调方法 83 private Callback callback = new Callback() { 84 @Override 85 public void onFailure(Call call, IOException e) { 86 setResult(e.getMessage(), false); 87 } 88 89 @Override 90 public void onResponse(Call call, Response response) throws IOException { 91 setResult(response.body().string(), true); 92 } 93 }; 94 95 //显示请求返回的结果 96 private void setResult(final String msg, final boolean success) { 97 runOnUiThread(new Runnable() { 98 @Override 99 public void run() { 100 if (success) { 101 Toast.makeText(UploadActivity.this, "请求成功", Toast.LENGTH_SHORT).show(); 102 } else { 103 Toast.makeText(UploadActivity.this, "请求失败", Toast.LENGTH_SHORT).show(); 104 } 105 resultTextView.setText(msg); 106 } 107 }); 108 } 109 110 //自定义的RequestBody,能够显示进度 111 public class ProgressRequestBody extends RequestBody { 112 //实际的待包装请求体 113 private final RequestBody requestBody; 114 //进度回调接口 115 private final ProgressListener progressListener; 116 //包装完成的BufferedSink 117 private BufferedSink bufferedSink; 118 119 /** 120 * 构造函数,赋值 121 * 122 * @param requestBody 待包装的请求体 123 * @param progressListener 回调接口 124 */ 125 public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) { 126 this.requestBody = requestBody; 127 this.progressListener = progressListener; 128 } 129 130 /** 131 * 重写调用实际的响应体的contentType 132 * 133 * @return MediaType 134 */ 135 @Override 136 public MediaType contentType() { 137 return requestBody.contentType(); 138 } 139 140 /** 141 * 重写调用实际的响应体的contentLength 142 * 143 * @return contentLength 144 * @throws IOException 异常 145 */ 146 @Override 147 public long contentLength() throws IOException { 148 return requestBody.contentLength(); 149 } 150 151 /** 152 * 重写进行写入 153 * 154 * @param sink BufferedSink 155 * @throws IOException 异常 156 */ 157 @Override 158 public void writeTo(BufferedSink sink) throws IOException { 159 if (bufferedSink == null) { 160 //包装 161 bufferedSink = Okio.buffer(sink(sink)); 162 } 163 //写入 164 requestBody.writeTo(bufferedSink); 165 //必须调用flush,否则最后一部分数据可能不会被写入 166 bufferedSink.flush(); 167 168 } 169 170 /** 171 * 写入,回调进度接口 172 * 173 * @param sink Sink 174 * @return Sink 175 */ 176 private Sink sink(Sink sink) { 177 return new ForwardingSink(sink) { 178 //当前写入字节数 179 long bytesWritten = 0L; 180 //总字节长度,避免多次调用contentLength()方法 181 long contentLength = 0L; 182 183 @Override 184 public void write(Buffer source, long byteCount) throws IOException { 185 super.write(source, byteCount); 186 if (contentLength == 0) { 187 //获得contentLength的值,后续不再调用 188 contentLength = contentLength(); 189 } 190 //增加当前写入的字节数 191 bytesWritten += byteCount; 192 //回调 193 progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength); 194 } 195 }; 196 } 197 } 198 199 //进度回调接口 200 interface ProgressListener { 201 void update(long bytesRead, long contentLength, boolean done); 202 } 203 204 }
如果我们在项目中直接使用上面的代码来进行http请求的话,势必会比较麻烦,所以这里我们需要封装上面的代码,尽量在项目中能用简短的代码完成网络请求。另外,一个项目中肯定会有很多个网络请求,我们没必要在每次网络请求中都创建一个OkHttpClient对象,所有的请求公用一个OkHttpClient就可以了。