Android开发之OkHttp的使用

本篇记录的是Android开发中OkHttp框架的使用,下面介绍OkHttp库的用法,本篇会给出OkHttp的使用demo,demo中包含了常用的get请求、post请求、文件的上传和下载,demo运行的效果如下图所示:

\\

\ \ \

下面上代码一一说明:

要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:

?
1
2
3
4
5
6
dependencies {
     compile fileTree(dir: 'libs' , include: [ '*.jar' ])
     testCompile 'junit:junit:4.12'
     compile 'com.android.support:appcompat-v7:23.1.1'
     compile 'com.squareup.okhttp3:okhttp:3.2.0'
}
这样就将OkHttp导入到项目中了。

 

(1)GET请求

最简单的GET请求用法如下:

 

?
1
2
3
4
5
6
7
8
//简单的Get请求,不带参数
public void simpleGetClick(View view) {
     okHttpClient = new OkHttpClient();
     Request request = new Request.Builder()
             .url( "http://192.168.1.170:8088/okhttp/test_simple_get.php" )
             .build();
     okHttpClient.newCall(request).enqueue(callback);
}
如果请求中要添加Header头和参数,可以用下面的方式:

 

 

?
1
2
3
4
5
6
7
8
9
//带参数的Get请求
public void addParamGetClick(View view) {
     okHttpClient = new OkHttpClient();
     Request request = new Request.Builder()
             .addHeader( "token" , "asdlfjkasdljfaskdjfalsjkljalk" //请求头中加入参数
             .build();
     okHttpClient.newCall(request).enqueue(callback);
}

需要注意的是,上面的代码中,callback是请求后的回调接口,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
//请求后的回调接口
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 );
     }
};
这个回调接口需要注意的是,onResponse和onFailure都不是在UI线程中执行的,所以如果我们要在onResponse或onFailure中进行UI相关的操作,需要在UI线程中进行。

(2)POST请求

比较简单的POST请求,用法如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//简单的带参数和Header的post请求
public void simplePostClick(View view) {
     okHttpClient = new OkHttpClient();
     RequestBody requestBody = new FormBody.Builder()
             .add( "username" , "wangwu" )
             .add( "password" , "hello12345" )
             .add( "gender" , "female" )
             .build();
     Request request = new Request.Builder()
             .url( "http://192.168.1.170:8088/okhttp/test_simple_post.php" )
             .post(requestBody)
             .addHeader( "token" , "helloworldhelloworldhelloworld" )
             .build();
     okHttpClient.newCall(request).enqueue(callback);
}
这里我们需要先构造一个RequestBody,然后把需要携带的参数放到RequestBody中,然后使用这个RequestBody构建一个Request请求,最后将这个请求放入队列中执行

 

如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//带文本参数和文件参数的post请求
public void filePostClick(View view) {
     RequestBody fileBody = RequestBody.create(MediaType.parse( "text/plain; charset=utf-8" ), tempFile);
     RequestBody requestBody = new MultipartBody.Builder()
             .setType(MultipartBody.FORM)
             .addFormDataPart( "username" , "wangwu" )
             .addFormDataPart( "password" , "hello12345" )
             .addFormDataPart( "gender" , "female" )
             .addFormDataPart( "file" , "info.txt" , fileBody)
             .build();
     Request request = new Request.Builder()
             .url( "http://192.168.1.170:8088/okhttp/test_param_post.php" )
             .post(requestBody)
             .addHeader( "token" , "helloworldhelloworldhelloworld" )
             .build();
     okHttpClient.newCall(request).enqueue(callback);
}
上面的代码中,tempFile是一个文本文件,为了POST提交文件和一些其他的参数,我们使用MultipartBody来构建一个请求体,需要注意的是,因为POST的内容含有文件,所以我们必须为这个请求体设置setType(MultipartBody.FORM)

(3)文件的上传

文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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;
 
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);
         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;
                     //回调
                     progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength);
                 }
             };
         }
     }
 
     //进度回调接口
     interface ProgressListener {
         void update( long bytesRead, long contentLength, boolean done);
     }
 
}
上面需要注意的是,上传文件需要实现自定义的RequestBody,也就是上面的ProgressRequestBody,在ProgressRequestBody中获取上传的进度。

(4)文件的下载

下载和上传类似,区别在于,需要我们实习自定义的ResponseBody而不是RequestBody了,下面上代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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就可以了。

猜你喜欢

转载自blog.csdn.net/smile_raccoon/article/details/52217422