Okhttp3
依赖配置
build.gradle
添加依赖:1
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
网络配置
添加res->xml->network-security-config.xml
文件:1
2
3
4
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
在AndroidMainifest.xml
中添加网络安全权限(一共2处)。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
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.njtech.myokhttp3">
<!-- 网络授权 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
简单的界面设计
activity_main.xml
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
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/h30"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/v50"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bt_test_GET_Syn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="测试GET同步无参"
app:layout_constraintEnd_toStartOf="@+id/v50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bt_test_GET_ASyn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="测试GET异步无参"
app:layout_constraintEnd_toStartOf="@+id/v50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bt_test_GET_Syn" />
<Button
android:id="@+id/bt_test_GET_ASyn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="测试GET异步有参"
app:layout_constraintEnd_toStartOf="@+id/v50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bt_test_GET_ASyn" />
<Button
android:id="@+id/bt_test_POST_ASyn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="测试POST异步"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/v50"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
GET和POST测试
MainActivity.java
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
205
206
207
208
209
210
211public class MainActivity extends AppCompatActivity implements View.OnClickListener{
// 不能用本地的127.0.0.1,需要用实际的IP地址
String url = "http://10.22.82.219:8080/";
private TextView tvInfo;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//隐藏状态栏
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
setContentView(R.layout.activity_main);
// 初始化控件
Button btTestGETSyn = findViewById(R.id.bt_test_GET_Syn);
btTestGETSyn.setOnClickListener(this);
Button btTestGETASyn = findViewById(R.id.bt_test_GET_ASyn);
btTestGETASyn.setOnClickListener(this);
Button btTestGETASyn2 = findViewById(R.id.bt_test_GET_ASyn2);
btTestGETASyn2.setOnClickListener(this);
Button btTestPOSTASyn = findViewById(R.id.bt_test_POST_ASyn);
btTestPOSTASyn.setOnClickListener(this);
tvInfo = findViewById(R.id.tv_info);
}
public void onClick(View view) {
switch (view.getId()){
case R.id.bt_test_GET_Syn:
testGETSyn();
break;
case R.id.bt_test_GET_ASyn:
testGETASyn();
break;
case R.id.bt_test_GET_ASyn2:
testGETASyn2();
break;
case R.id.bt_test_POST_ASyn:
testPOSTASyn();
break;
}
}
// 测试GET同步方法
private void testGETSyn() {
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url + "doctor-user/listAll")
.get() //默认就是GET请求,可以不写
.build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
public void run() {
try {
Response response = call.execute();
final String info = response.body().string();
// 主线程更新UI
runOnUiThread(new Runnable() {
public void run() {
tvInfo.setText(info);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
// 测试GET异步方法(无参)
private void testGETASyn() {
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url + "doctor-user/listAll")
.get()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
// 主线程更新UI
runOnUiThread(new Runnable() {
"SetTextI18n") (
public void run() {
tvInfo.setText("Error");
}
});
}
public void onResponse(Call call, final Response response) throws IOException {
final String info = response.body().string();
// 主线程更新UI
runOnUiThread(new Runnable() {
public void run() {
tvInfo.setText(info);
}
});
}
});
}
// 测试GET异步方法(有参)
private void testGETASyn2() {
String requestBody = "10001";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url + "doctor-user/get/byEmployeeId/" + requestBody)
.get()
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
// 主线程更新UI
runOnUiThread(new Runnable() {
"SetTextI18n") (
public void run() {
tvInfo.setText("Error");
}
});
}
public void onResponse(Call call, Response response) throws IOException {
try {
// 数据解析
String info = response.body().string();
JSONObject jsonObject = null;
jsonObject = new JSONObject(info);
final String data = jsonObject.get("data").toString();
// 主线程更新UI
runOnUiThread(new Runnable() {
public void run() {
tvInfo.setText(data);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
// 测试异步POST
private void testPOSTASyn(){
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("name", "陈修翔")
.add("gender", "男")
.build();
Request request = new Request.Builder()
.url(url + "doctor-user/update/byName")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
// 主线程更新UI
runOnUiThread(new Runnable() {
"SetTextI18n") (
public void run() {
tvInfo.setText("Error");
}
});
}
public void onResponse(Call call, Response response) throws IOException {
try {
// 数据解析
String info = response.body().string();
System.out.println(info);
JSONObject jsonObject = null;
jsonObject = new JSONObject(info);
final String data = jsonObject.get("message").toString();
// 主线程更新UI
runOnUiThread(new Runnable() {
public void run() {
tvInfo.setText(data);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
注:这里异步POST测试没有调通!
参考博客
Retrofit+RxJava
Retrofit简介
Retrofit
是现下Android
端开发非常流行的一款网络请求框架,它通过动态代理的方式将Java
接口翻译成网络请求,通过OkHttp
发送请求,并且其具备强大的可扩展性,支持各种数据格式的转换以及RxJava
。
网络请求框架是一套提供给开发者使用的用于网络请求的API
接口,Android
网络请求一般是基于Http
协议的,而Http
协议属于应用层的协议,具体的数据传输需要依赖传输层的TCP
协议,Android
系统提供了Socket
编程接口给开发者建立TCP
请求,所以具体数据的发送需要依赖Socket
;Http
协议属于应用层的协议,它是用来规定数据的传输格式的,用于传输双方都能按照固定的格式解读数据;所以,一个网络请求框架至少要包含以下几个功能:
- 提供接口给开发者传入请求参数;
- 编写
Socket
代码,建立TCP
; - 通过
TCP
连接,严格按照Http
协议的格式将请求参数发送给服务端; - 严格按照
Http
协议的格式解读服务端返回的数据; - 提供相应的接口给开发者获得返回数据(一般是通过回调处理的)。
上面五点是一个网络请求框架必须具备的功能,当然,一个好的网络请求框架还应该具备如下特点:
- 提供给开发者使用的
API
尽可能简单; - 添加了对网络缓存的处理,避免不必要的请求;
- 具有较高的性能;
- 具有较高的可扩展性。
我们常用的OkHttp
、HttpURLConnection
等网络请求框架,其内部就会将开发者传入的请求参数按照Http
协议的格式组织好,并通过TCP
连接发送给服务端,在收到服务端返回数据后,又会按照Http
协议去解读数据,并提供相应的API
(一般是回调)给开发者获得数据。由于OkHttp
属于比较底层的网络请求框架,开发者在使用时还是会比较复杂,于是Retrofit
对OkHttp
进行了再度的封装,使得开发者在使用时更加的方便。
App
应用程序通过Retrofit
请求网络,实际上是使用Retrofit
接口层封装请求参数,之后由OkHttp
完成后续的请求操作。在服务端返回数据之后,OkHttp
将原始的结果交给Retrofit
,Retrofit
根据用户的需求对结果进行解析。完成数据的转化(converterFactory
),适配(callAdapterFactory
),通过设计模式进行各种扩展。
使用Retrofit的七步骤
- 添加
Retrofit
依赖,网络权限 - 定义接收服务器返回数据的
Bean
- 创建网络请求的接口,使用注解(动态代理,核心)
builder
模式创建Retrofit
实例,converter
,calladapter
…- 创建接口实例,调用具体的网络请求
call
同步/异步网络请求- 处理服务器返回的数据
Retrofit网络通信八步骤
- 创建
Retrofit
实例 - 定义网络请求接口,并为接口中的方法添加注解
- 通过动态代理生成网络请求对象
- 通过网络请求适配器将网络请求对象进行平台适配
- 通过网络请求执行器,发送网络请求(
call
) - 通过数据解析器解析数据
- 通过回调执行器,切换线程
- 用户在主线程处理返回结果
依赖配置
build.gradle
添加依赖:1
2
3
4
5
6
7//请求网络
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
//RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
网络配置
参考上文Okhttp3
网络配置。
GET和POST测试
实体类
DoctorInfo.java
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
130public class DoctorInfo {
private Long id;
private String name;
private String gender;
private int age;
private String job;
private String department;
private int employeeId;
private String cardId;
private String fingerId;
private String password;
// 不能用date,要用String!否则会Json解析错误!
private String updateTime;
private String createTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getFingerId() {
return fingerId;
}
public void setFingerId(String fingerId) {
this.fingerId = fingerId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
// 需要重写toString()!
public String toString() {
return "DoctorInfo{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", job='" + job + '\'' +
", department='" + department + '\'' +
", employeeId=" + employeeId +
", cardId='" + cardId + '\'' +
", fingerId='" + fingerId + '\'' +
", password='" + password + '\'' +
", updateTime='" + updateTime + '\'' +
", createTime='" + createTime + '\'' +
'}';
}
}
DoctorData.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class DoctorData{
// 要和后端字段保持一致!
private List<DoctorInfo> doctorUserList;
public List<DoctorInfo> getDoctorUserList() {
return doctorUserList;
}
public void setDoctorUserList(List<DoctorInfo> doctorUserList) {
this.doctorUserList = doctorUserList;
}
public String toString() {
return "DoctorData{" +
"doctorUserList=" + doctorUserList +
'}';
}
}
DoctorResponse.java
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
39public class DoctorResponse {
private boolean success;
private int code;
private String message;
// 要和后端字段保持一致!
private DoctorData data;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public DoctorData getData() {
return data;
}
public void setData(DoctorData data) {
this.data = data;
}
}
注1:所有字段必须要和后端字段保持一致!
注2:需要重写实体类的toString()
方法。
注3:Date
类型的字段要用String
类型替换!否则会Json
解析错误。
网络工具类
SeerService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public interface SeerService {
// 步骤1:创建 用于描述网络请求 的接口
// 注解里传入 网络请求 的部分URL地址
// Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
// 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
// 采用Observable<...>接口
// getAllDoctorInfo()是接受网络请求数据的方法
"doctor-user/listAll") (
Observable<DoctorResponse> getAllDoctorInfo();
"doctor-user/get/byEmployeeId/{employee_id}") (
Observable<DoctorResponse> getDoctorInfoByEmployeeId(@Path("employee_id") Integer employeeId);
"/doctor-user/update/byName") (
Observable<DoctorResponse> updateDoctorInfoByDoctorName(@Body DoctorInfo doctorInfo);
}
SeerServiceCreator.java
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// 写成单例类,避免重复创建,浪费资源
public class SeerServiceCreator {
private static SeerServiceCreator instance;
private Retrofit retrofit;
private SeerServiceCreator(){
retrofit = new Retrofit.Builder()
.baseUrl(Constant.SEER_SERVER_IP) // 设置 网络请求 Url
.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava
.build();
}
// 双检锁/双重校验锁
public static SeerServiceCreator getInstance(){
if (instance == null) {
synchronized (SeerServiceCreator.class){
if (instance == null) {
instance = new SeerServiceCreator();
}
}
}
return instance;
}
public <T> T create(Class<T> tClass) {
return retrofit.create(tClass);
}
}
MainActivity实现
1 | public class MainActivity extends AppCompatActivity implements View.OnClickListener{ |
这里只是简单的逻辑测试,不能当成业务代码处理。