HttpURLConnection详解
HttpURLConnction在Android4.4之后底层实现采用的是OkHttp
因为是学习Okhttp和Retrofit的,对比HttpURLConnction的时候,是吧,你懂得,不能总是夸他好,不提他的坏处无法衬托出okhttp3的好处,,通过贬低一个人来烘托出另一个人,大人的世界还真是肮脏啊。。。言归正传:
HttpURLConnction缺点:
1.首先我觉得最重要的就是耦合性很高,尤其是配合handler,asynctask的时候,如果后期需要替换为其他的网络请求方案,处理起来将会十分棘手
2.使用繁琐,缺乏连接池管理功能
OkHttp3的优点:
使用连接池复用连接,效率很高
提供了Http响应的缓存机制,可以减少不必要的网络请求
自动重试功能,当网络出现问题时,Okhttp会自动重试一个主机的多个地址
底层使用socket连接,更加高效
缺点:
回调处理并不在主线程,封装起来比较麻烦,不过年轻人怎么能怕麻烦呢,用handler处理一下呗。
一、HttpURLConnctin
1.首先对于url构造和请求实体构造等过程,HttpURLConnection或OkHttpClient还需要手动拼接url,还需要手动对上传或返回的数据流进行操作。
2.如果想进行解析,还需要自己再去手动引入一些解析框架如Gson等。此外,还需要用异步控制的方案或者框架如AyncTask/handler/Rxjava等,手动再与网络请求进行耦合。如果设计不当,会导致这几个框架之间耦合较大,导致如果有一天想替换掉某一部分(比如想用Rxjava替换Call或Gson替换Jackson)会很困难
二、Retrofit:
Retrofit在这几个方面都比较有优势:首先在构造HTTP请求时,我们只需要去构造接口的方法,框架会帮我们自动去实现这些方法。按规则去构造url,指定请求参数。可以直接用解析框架生成请求实体或解析结果。得到想要的异步请求的对象(Call/RxJava/RxJava2/guava/CompletableFuture)。请求构造更方便,同时与解析框架和异步请求框架解耦(通过Retrofit.addxxxFactory指定用不同的框架),可以更便捷的替换不同的解析框架或者异步框架
重点:
如果后台api是restful风格的,就建议使用retrofit,否则还是用okhttp
别的先不说,先搭建一个服务器,因为我们需要用okhttp下载文件和上传文件,没有服务器的话,我们没法测!
环境:
Eclipse+Tomcat9.0+struts2.5
步骤:
1.解压struts包之后,找到apps–>struts-blank.war解压
2.将WEB-INF-》lib下的jar包拷到项目对应目录下
3.将WEB-INF-》classes文件夹下的struts.xml文件拷到项目src文件夹下
4.将WEB-INF-》web.xml里的:
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
拷到对应的web.xml中。
5.新建操作类,实现Action方法:
package com.zzg.actions;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport{
/**
*
*/
private static final long serialVersionUID = 1L;
private String userName;
private String password;
/**
* 模仿用户登录
* @return
* @throws IOException
*/
public String login() throws IOException {
System.out.println(userName+","+password);
//获取请求头
HttpServletRequest request=ServletActionContext.getRequest();
System.out.println("请求头:--->S-ZZG-TOKEN:"+request.getHeader("S-ZZG-TOKEN"));
//返回给客户端信息
HttpServletResponse response= ServletActionContext.getResponse();
PrintWriter writer=response.getWriter();
writer.write("login success!");
writer.flush();
return null;
}
public String getUserName() {
return userName;
}
public void setUserName(String username) {
this.userName = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
struts.xml里的配置:
这里method方法名对应上,name倒是随意,不过还是保持一致的比较好。
首先在复习一下Get和Post方式的区别:
1.Post或者Get只是Http协议中向后台服务器发送数据的一种机制,是Http协议的一个组成部分。
2.Post请求是将要提交到后台服务器的数据放在Http包的包体中。
3.Get请求是将数据放在URL之后,比如http://androidxx.cn/forum.php?mo … d=11&extra=page%3D1,可以看到此URL由2部分组成,分别是http://androidxx.cn/forum.php和?后面的参数。这就是典型的Get请求方式。
因为Get请求时直接将参数放在URL后面,而URL的长度是有一定的限制,所以当传递的数据特别大的时候,Get请求就不能完成。
4.相比较,Post请求的参数是放在Http包的内部,不能轻易被看到;Get请求的参数直接是跟在URL之后,可以很容易被用户获取。所以,相对而言,Post的请求方式更安全。
Get和Post的使用场景:
Get(简单查询):
下载图片、获取一些安全性不高的文件
Post(注重安全的或者请求参数很大的):
1.登录 2.注册 3.上传 4.支付 5.下单 6.获取用户信息…
言归正传,Okhttp中get和post的最大区别在于,post的时候需要传递一个RequestBody参数,因为get参数是暴露在url里的,post为了安全起见是将参数放在RequestBody对象里的,而RequestBody又有两个子类:FormBody、MultipartBody。
一、简单get、post请求如下:
/**
* 同步GET请求execute
* @param view
* @throws IOException
*/
public void doGet(View view) {
new Thread(()->{
Request request = new Request.Builder()
.get()
.url(URL + "login?userName=zzg&password=1234")
.addHeader("S-ZZG-TOKEN", ServiceContext.getUUID())
.build();
Response response= null;
try {
response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()){
Log.e(TAG, "doGet: "+response.code());
Response finalResponse = response;
getActivity().runOnUiThread(()->{
ToastUtils.shortMsg("连接服务器失败!错误码:--"+String.valueOf(finalResponse.code()));
});
}else{
String resp=response.body().string();
getActivity().runOnUiThread(()->{
tv1.setText(resp);
});
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
/**
* 异步Post请求
* @param view
*/
public void doPost(View view) {
new Thread(()->{
RequestBody requestBody = new FormBody.Builder()
.add("userName", "空山鸟语")
.add("password", String.valueOf(12132))
.build();
Request request = new Request.Builder()
.url(URL + "login")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//e.getMessage获得错误信息,e.printStackTrace打印出错误路径
Log.i(TAG, "onFailure: ....." + e.getMessage());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "连接服务器失败", Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String s = response.body().string();
Log.i(TAG, "onResponse: " + s);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
try {
tv1.setText("服务器返回数据:"+s);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
}).start();
}
上面两个是最基本的get和post请求,get就不多说了,post中构造了一个RequestBody参数,将两个参数userName,password封装在其内部,实现是其子类FormBody。
二、传递字符串给服务器
客户端代码:
//向服务器传递字符串
private static final String jsonString="{\"username\":\"zzg\",\"job\":\"coder\"}";
/**
* 上传json字符串
* @param view
*/
public void doPostString(View view) {
new Thread(()->{
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;chaset=utf-8"), jsonString);
Request request = new Request.Builder()
.url(URL + "postString")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//e.getMessage获得错误信息,e.printStackTrace打印出错误路径
Log.i(TAG, "onFailure: ....." + e.getMessage());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "连接服务器失败", Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String s = response.body().string();
Log.i(TAG, "onResponse: " + s);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
try {
tv1.setText(s);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
}).start();
}
服务端代码:
/**
* 从客户端接收jsonString
* @return
* @throws IOException
*/
public String postString() throws IOException {
StringBuilder result=new StringBuilder();
HttpServletRequest request=ServletActionContext.getRequest();
InputStream inputStream=request.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
String buf;
while((buf=reader.readLine())!=null) {
result.append(buf);
}
//返回给客户端信息
HttpServletResponse response= ServletActionContext.getResponse();
PrintWriter writer=response.getWriter();
writer.write("服务端接收数据:"+result);
writer.flush();
System.out.println("从客户端接收的数据为:\n"+result);
return null;
}
三、上传文件
客户端逻辑:
/**
* 上传文件
* @param view
*/
public void doPostFile(View view) {
//本地必须得有这个图片
File file = new File(SDHelper.getImageFolder(), "portrait.png");
if (!file.exists()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "文件不存在", Toast.LENGTH_SHORT).show();
}
});
}
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
Request request = new Request.Builder().url(URL + "postFile").post(requestBody).build();//拿到号码
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "连接服务器失败", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "上传成功", Toast.LENGTH_SHORT).show();
}
});
}
});
}
服务端逻辑:
/**
* 接收客户端发送过来的文件
* @throws IOException
*/
public void postFile() throws IOException {
HttpServletRequest request=ServletActionContext.getRequest();
InputStream inputStream=request.getInputStream();
//File
// String fileName="D:/okhttpServer/zzg.jpg";
//
// File file = new File(fileName);
// if (!file.exists()) {
// // File dirs=new File(fileName.substring(0,fileName.lastIndexOf("/")));
// File dirs=file.getParentFile();
// if (!dirs.isDirectory()||!dirs.exists()) {
// dirs.mkdirs();
// }
// file.createNewFile();
// }
String dir=ServletActionContext.getServletContext().getRealPath("files");
File file=new File(dir,"zzzz.jpg");
FileOutputStream fos = new FileOutputStream(file);
int len=0;
byte[] buf=new byte[1024*2];
while((len=inputStream.read(buf))!=-1) {
fos.write(buf, 0, len);
}
inputStream.close();
fos.flush();
fos.close();
}
这里需要提示一下:
1.String dir=ServletActionContext.getServletContext().getRealPath(“files”);,这个目录是Tomcat加载项目的路径,指定这个目录是为了方便我们通过拼接url从服务器下载文件。在底下的下载文件中可以看出来。
2.RequestBody.create(MediaType.parse("application/octet-stream"), file);
这里解释一下,create方法第一个参数需要传入的是MediaType。
MediaType指的是要传递的数据的MIME类型,MediaType对象包含了三种信息:type 、subtype以及charset,一般将这些信息传入parse()方法中,这样就可以解析出MediaType对象,比如 “text/x-markdown; charset=utf-8” ,type值是text,表示是文本这一大类;/后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类; charset=utf-8 则表示采用UTF-8编码 。
一些常用的MIME类型:
json : application/json
xml : application/xml
png : image/png
jpg : image/jpeg
gif : imge/gif
详细的请戳:
MIME参考手册
四、上传文件附带其他信息
客户端:
/**
* 上传文件...跟上面差在可能是也传参数过去吧
* @param view
*/
public void doUpLoad(View view) {
File file = new File(SDHelper.getImageFolder(), "portrait.png");
MultipartBody multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("userName", "SprrowZ")
.addFormDataPart("password", "0517")
.addFormDataPart("mPhoto", "SprrowZ.png", RequestBody.create(//mPhoto是Key,服务器端通过这个取内容
//上传成功后,文件名还是为yy.jpg
MediaType.parse("application/octet-stream"), file))
.build();
Request request = new Request.Builder().url(URL + "uploadInfo").post(multipartBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, "onFailure: ....." + e.getMessage());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "连接服务器失败", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "上传成功", Toast.LENGTH_SHORT).show();
}
});
}
});
}
服务端:
/**
* 上传文件和其他信息
* @return
* @throws IOException
*/
public String uploadInfo() throws IOException{
if (mPhoto==null) {
System.out.println(mPhotoFileName+"is null!");
}
String dir="D:/okhttpServer/"+mPhotoFileName;
File file=new File(dir);
if (!file.exists()) {
// File dirs=new File(fileName.substring(0,fileName.lastIndexOf("/")));
File dirs=file.getParentFile();
if (!dirs.isDirectory()||!dirs.exists()) {
dirs.mkdirs();
}
file.createNewFile();
}
FileUtils.copyFile(mPhoto, file);
System.out.println("fileName"+mPhotoFileName+"||userName:"+userName
+"||password:"+password);
return null;
}
五、下载文件:
/**
*下载文件
* @param view
*/
public void doDownLoad(View view) {
final Request request = new Request.Builder().url(URL + "files/zzzz.jpg").build();//电脑上这个路径下要有test.jpg
//输出路径
Log.i(TAG, "doDownLoad: " + URL + "files/zzg.jpg");
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
getActivity().runOnUiThread(()->{
Toast.makeText(getActivity(), "连接服务器失败", Toast.LENGTH_SHORT).show();
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
getActivity().runOnUiThread(()->{
Toast.makeText(getActivity(), "下载成功!已保存到"+SDHelper.getImageFolder()+"目录下!",
Toast.LENGTH_SHORT).show();
});
//******************追踪进度*********************
long total = request.body().contentLength();//文件的大小
InputStream is = response.body().byteStream();
monitorProgress(is,total);
}
});
}
/**
* 监控文件下载进度
* @param is
* @param total
*/
private void monitorProgress(InputStream is,long total){
long sum = 0L;
int len = 0;
File file = new File(SDHelper.getImageFolder(), "fromServer.png");//这个名字本地不能有
byte[] buf = new byte[128];
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
//输出进度
sum += len;
Log.e(TAG, sum + "/" + total);//输出下载进度
}
fos.flush();
fos.close();
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
这里,就可以看出来下载的路径URL + "files/zzzz.jpg"
,如果下载到d盘,struts要改配置,目前能力不足,只能出此下计,在存的时候对应上那个路径就好了。
快速入门
本文主要是参考慕课网Jennynick老师视频所做出的总结。
一、首先明白一点:
Retrofit是基于Okhttp网络框架进行的二次封装,其本质仍是Okhttp。(类似乌尔奇奥拉的二段归刃)
另外,科普一下,Android5.0之后不再使用HttpClient了。本来还想着看看httpclient的高阶用法呢,你看,多学习就可以少学习。
二、同Volley对比:
Volley基于HttpUrlConnction,Google官方推出,只适用于轻量级网络交互,不适合大文件下载上传场景。
没有对比就没有伤害,伤害了Volley突出了Retrofit如下优点:
API设计简洁易用、注解化配置高度解耦、支持多种解析器、支持Rxjava
实际操作:
一、依赖包导入
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
网络权限别忘了申请~
二、创建接口设置请求类型与参数
ex:新建UserInfoModel类和UserMgrService接口:
@GET("login")
public Call login(@Query("username") String username,@Query("pwd") String pwd);
(login是UserMgrService里的一个方法)
三、创建Retrofit对象、设置数据解析器
Retrofit retrofit=new Retrofit.Builder().baseUrl(Constants.BASE_URL).addConverterFactory(GsonConverterFactory.create())).build();
数据解析器支持类型有:
Gson、Jackson、Simple XML、 Protobuf、Moshi、Wire、Scalars
四、生成接口对象
UserMgrService service=retrofit.create(UserMgrService.class);
五、调用接口方法返回Call对象
Call call=service.login(“zhangsan”,“2345”);
六、发送请求(同步、异步)
同步:调用Call对象的execute(),返回结果的响应体(如果是耗时操作,可能导致ANR,需要处理)
异步:调用Call对象的enqueue(),参数是一个回调
请求方法有如下几个:
1.@GET 表示这个一个get请求
2.@POST 表示这是一个post请求
3.@PUT …
4.@DELETE …
5.@HEAD …
6.@OPTIONS …
7.@PATCH …
请求参数如下:
8.@Headers 添加请求头
9.@Path 替换路径
10.@Query :形成单个查询参数,将接口url中追加类似于"page=1"的字符串,形成提交给服务器端的参数,主要用于Get请求数据,用于拼接在拼接在url路径后面的查询参数,一个 @Query相当于拼接一个参数
11.@QueryMap:包含多个@Query注解参数(传递较多参数时使用)
12.@Field、@FieldMap
@Field的用法类似于@Query,就不在重复列举了,主要不同的是@Field主要用于Post请求数据。
@FieldMap的用法类似于@QueryMap。
两者主要区别是:如果请求为post实现,那么最好传递参数时使用@Field、@FieldMap和@FormUrlEncoded。因为@Query和或QueryMap都是将参数拼接在url后面的,而@Field或@FieldMap传递的参数时放在请求体的。
13.@FormUrlEncoded: 以form表单方式上传,用于修饰Field注解和FieldMap注解, 如果去掉@FromUrlEncoded在post请求中使用@Field和@FieldMap,那么程序会抛出Java.lang.IllegalArgumentException: @Field parameters can only be used with form encoding. 的错误异常。
所以如果平时公司如果是Post请求的话,千万别忘记了加这@FromUrlEncoded注解。
@Body 可以用来提交 Json 数据[可以是实体类,retrofit会处理为json字符串]、上传文件
14.@Multipart
表示请求发送multipart数据,需要配合使用@Part @PartMap
15.@Part
用于上传文件,与@MultiPart注解结合使用
@Multipart
@POST("/url")
Call uploadFlie(
@Part("description") RequestBody description,
@Part("files") MultipartBody.Part file);
16.@PartMap
用于上传文件,与@MultiPart注解结合使用,默认接受的类型是Map
@Multipart
@POST("{url}")
Call uploadFiles(
@Path("url") String url,
@PartMap() Map maps);
七、请求实例:
① 新建一个接口,声明请求方法
② 在activity中声明retrofit,并调用接口里的方法。
测试调用github的api:(以下Demo均是在此Url基础上拼接)
https://api.github.com/
无参:(最简单的get请求)
@GET("/")//不传参数
Call getMessages();
@Path替换路径:(URL中有参数)
@GET("repos/{owner}/{repo}")//路径为:https://api.github.com/repos/{owner}/{repo}
Call getMessages3(@Path("owner") String owner,@Path("repo") String repo);//替换路径
@Path和@Query一块使用:(URL中有参数且URL问号之后有参数)
@GET("users/{user}/repos")//路径:https://api.github.com/users/{user}/repos{?type,page,per_page,sort}
Call getMessages4(@Path("user") String user,
@Query("page") int page,@Query("per_page")int per_page);
@QueryMap的使用(URL中问号之后有多个参数且个数不固定)
@GET("users/{user}/repos")
Call getMessages5(@Path("user") String user, @QueryMap HashMap info);
_________________________________
HashMap params=new HashMap<>();
params.put("page",1);
params.put("per_page",3);
Call call=api.getMessages5("SprrowZ",params);
好,今天先更到这里。
继续来个实例:
public interface GithubApi {
/**
* GET请求
* @return
*/
//访问文件地址:https://raw.githubusercontent.com/SprrowZ(用户名)/AndroidZex(仓库名)/master(分支)/.gitignore(路径)
@GET("/")//不传参数
Call getMessages();
@GET("users/SprrowZ")//这个参数是用来拼路径的,https://api.github.com/users/{user},就是这里的{user}
Call getMessages2();
@GET("repos/{owner}/{repo}")//路径为:https://api.github.com/repos/{owner}/{repo},{owner}/{repo}
Call getMessages3(@Path("owner") String owner,@Path("repo") String repo);//替换路径
@GET("users/{user}/repos")//路径:https://api.github.com/users/{user}/repos{?type,page,per_page,sort}
Call getMessages4(@Path("user") String user,
@Query("page") int page,@Query("per_page")int per_page);
@GET("users/{user}/repos")
Call getMessages5(@Path("user") String user, @QueryMap HashMap info);
}
private void retrofitGet1() {
new Thread(()->{
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Constant.GITHUB_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
GithubApi service=retrofit.create(GithubApi.class);
Call call=service.getMessages();
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println(response.body().toString());
try {
content1.setText("get无参:"+"\n"+response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
t.printStackTrace();
}
});
}).start();
}
重点讲一下这个:.addConverterFactory(GsonConverterFactory.create())
Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型,看最上面我们接受返回类型的时候不是ResponseBody而是UserInfoModel,这个类是我们建的实体类,用来接受返回的数据,当数据不复杂时用这个更方便操作,如果复杂的话直接用ResponseBody比较好,个人感觉。。
FBI WARNING
一、response.body().string()
只有第一次有取到值!!!!所以务必赋值给一个String ,然后操作这个对象!
如果不信的话,可以打两个log,都输出response.body().string()
你就会发现第二次没有值,这个是正常的,看源码就晓得了,节省资源的策略。
二、可以看到请求路径中有大括号’{}’,这里我们需要在方法里声明这个路径【@Path】,传入值即可。@Path 路径中需要替换{}的情况,{}内要和@Path("")内对应一致!
一、post请求上传json字符串
package com.rye.catcher.activity.fragment.orr.interfaces;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
/**
* Created at 2019/1/10.
*
* @author Zzg
* @function:
*/
public interface zServerApi {
/**
* post提交jsonString给服务器
* @param path
* @param user
* @param pass
* @param postBean
* @return
*/
@POST("{path}")
Call postMessage(@Path("path") String path,
@Query("userName") String user,
@Query("password") String pass,
@Body PostBean postBean);
}
package com.rye.catcher.activity.fragment.orr.interfaces;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created at 2019/1/10.
*
* @author Zzg
* @function:
*/
public class PostBean implements Parcelable {
private String city;
private String job;
private boolean sex;
public PostBean(){
}
protected PostBean(Parcel in) {
city = in.readString();
job = in.readString();
sex = in.readByte() != 0;
}
public static final Creator CREATOR = new Creator() {
@Override
public PostBean createFromParcel(Parcel in) {
return new PostBean(in);
}
@Override
public PostBean[] newArray(int size) {
return new PostBean[size];
}
};
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
@Override
public String toString() {
return "PostBean{" +
"city='" + city + '\'' +
", job='" + job + '\'' +
", sex=" + sex +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(city);
dest.writeString(job);
dest.writeByte((byte) (sex ? 1 : 0));
}
}
private static final String BASE_URL="http://192.168.43.231:8088/OkhttpServer/";
private void postString() {
new Thread(()->{
Retrofit retrofit =new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpUtil.getInstance().getClient())
.build();
zServerApi serverApi=retrofit.create(zServerApi.class);
PostBean bean=new PostBean();
bean.setCity("ShangHai");
bean.setJob("程序员");
bean.setSex(true);
try {
Response response= serverApi.postMessage("postString","zzg","0517",bean)
.execute();
if (!response.isSuccessful()){
throw new IOException("Unexpected result:"+response.code());
}
Log.i(TAG, "postString: "+response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
上面就是给服务器上传一个实体类,retrofit会将实体类中的信息转换为json字符串,因为要在网络中传输,所以实体类中要实现parcelabel接口。
@Body PostBean postBean
实体类是通过这句话传递的。这里:
@Body
非表单请求体;
使用 @Body 注解,指定一个对象作为 request body 。
作用:以 Post方式 传递 自定义数据类型 给服务器
注意:如果提交的是一个Map,那么作用相当于 @Field
就是用来向服务器传递对象用的。
Tips:
实际上,如果我们在post请求上加上 @FormUrlEncoded,连接服务器进行上传的时候,会报错:
@Body parameters cannot be used with form or multi-part encoding.
从提示语句中可以看出,@Body标签不可以和 @FormUrlEncoded或者
二、上传文件