• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
戈瑾
博客园    首页    新随笔    联系   管理    订阅  订阅
Android学习——使用okhttp

要使用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就可以了。

posted on 2021-06-14 22:33  戈瑾  阅读(566)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3