retrofit2 使用完全解析(1)

59 篇文章 0 订阅
33 篇文章 1 订阅

前言

上一篇文章博主介绍了okhttp这个同步的网络请求框架的使用,也说了okhttp的优点,但是同时也指出了okhttp在使用方面的不便,所以本文介绍基于okhttp的网络框架retrofit的使用,还是同一个公司的产品.

1.支持异步请求

2.用户自己定义请求接口,由框架自动通过代理实现,这就相当于把网络请求的部分代码抽取出来,代码更加简洁

3.利用注解描述请求的参数,极大的方便了用户的使用


本文重在讲解如何使用retrofit,原理将会在后续的博客中发表,请感兴趣的人关注一下


一个牵扯到网络请求的app,在很多的Activity或者Fragment中都可能会用到网络请求,所以这里就使用在项目中比较常见的搭建方式


声明请求接口

/**
 * Created by cxj on 2016/6/10.
 * 所有的网络请求
 */
public interface NetWorkService {

    /**
     * 一个get请求的请求接口,返回是字符串
     *
     * @return
     *
     */
    @GET("https://publicobject.com/helloworld.txt")
    public Call<String> get();

}

暂时先书写一个返回字符串的请求接口,由于这里最后的结果需要转化成字符串,在框架中需要添加转化的组建,在官网中提供了以下几种

最后一个有转化成String的功能,所以这里我们要用到它,添加对应的依赖
compile 'com.squareup.retrofit2:converter-scalars:2.0.0'
这个依赖的可以从maven库中去寻找,也可以在Android Studio中搜索哦
菜单 --> Build --> Edit Libraries and Dependencies...






快速搭建环境

编写MyApp继承Application,别忘了在项目清单文件中的application节点中添加name属性,并且添加网络请求的权限

/**
 * Created by cxj on 2016/6/10.
 */
public class MyApp extends Application {

    /**
     * 声明请求的接口
     */
    public static NetWorkService netWorkService;

    /**
     * 网络请求框架
     */
    public static Retrofit retrofit;

    @Override
    public void onCreate() {
        super.onCreate();

        retrofit = new Retrofit.Builder()
                .baseUrl("http://192.168.1.102:8080/Retrofit/")
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();

        //让框架自动实现我们的请求接口,让我们的请求接口可以被调用
        netWorkService = retrofit.create(NetWorkService.class);

    }
}

上述代码中其实就是创建了初始化了请求的接口,并且添加了一个转化器,这样子在任何地方就可以直接使用了

并且可以看到我们有一个baseUrl,这是相当于前缀的意思,如果你请求的地址不是绝对的,那么他会添加上这个前缀,如果你的请求是绝对的地址,那么这个baseUrl就会被自动忽略

Get请求

xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <Button
        android:layout_width="match_parent"
        android:text="get请求测试<span style="font-family: Arial, Helvetica, sans-serif;">"</span>
        android:onClick="getTest"
        android:layout_height="wrap_content" />
    
</RelativeLayout>

就是一个按钮,点击请求网络


Activity中代码

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void getTest(View view) {

        Call<String> call = MyApp.netWorkService.get();

        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                String result = response.body();
                System.out.println("result = " + result);
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                System.out.println("请求失败");
            }
        });

    }

}

相比我们之前使用okhttp来请求,是不是变得简单了许多呢?

来看看请求的结果,可以看到请求成功


Post请求提交普通键值对

这里就需要用到自己的服务器啦,这里还是使用okhttp博文中使用的服务器,博文结束会分享

先使用浏览器测试一下返回的结果,先有一个直观的了解

第一次使用name:cxj pass:123 来注册


第二次使用name:cxj pass:123 注册


可以看到返回的数据中有失败的提示和响应的错误信息,表示用户名已经存在,那么接口这边没有问题啦,那我们就开工写客户端的代码吧!


在网络请求接口中添加测试方法

public interface NetWorkService {

    /**
     * 一个get请求的请求接口,返回是字符串
     *
     * @return
     */
    @GET("https://publicobject.com/helloworld.txt")
    public Call<String> get();

    /**
     * post提交数据
     *
     * @param name
     * @param pass
     * @return
     */
    @FormUrlEncoded
    @POST("test/register")
    public Call<String> register(@Field("name") String name, @Field("pass") String pass);

}

可以看淡添加了一个注册的方法,使用POST请求,路径是相对路径,所以会拼接上前缀,提交的数据有name和pass,需要用@Field注解标明


xml文件中添加要给按钮,这里不再贴出,按钮调用注册的方法

Activity中注册方法代码

    public void register() {
        
        Call<String> call = MyApp.netWorkService.register("cxj", "123");
        
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                //拿到返回的json数据
                String result = response.body();
                //打印结果
                System.out.println("result = " + result);
                //利用Gson转化json为实体对象
                Gson gson = new GsonBuilder().create();
                //传化后的实体对象
                Msg msg = gson.fromJson(result, Msg.class);
                //提示实体对象中的信息
                Toast.makeText(MainActivity.this, msg.getMsgText(), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                System.out.println("注册失败");
            }
        });
    }

如果请求成功,就会提示msg对象中的信息,这里牵扯到两个实体对象,这里贴一下代码

Msg:

public class Msg {

	public static final String OK = "ok";

	public static final String ERROR = "error";

	/**
	 * 只有两个值<br>
	 * 1."ok"<br>
	 * 2."error"
	 */
	private String msg = ERROR;

	/**
	 * 信息的文本
	 */
	private String msgText = ERROR;

	private Object data;

	public Msg() {
		super();
	}

	public Msg(String msg, Object data) {
		super();
		this.msg = msg;
		this.data = data;
	}

	public Msg(String msg, String msgText, Object data) {
		super();
		this.msg = msg;
		this.msgText = msgText;
		this.data = data;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public String getMsgText() {
		return msgText;
	}

	public void setMsgText(String msgText) {
		this.msgText = msgText;
	}

	@Override
	public String toString() {
		return "Msg [msg=" + msg + ", msgText=" + msgText + ", data=" + data + "]";
	}
}
User:
/**
 * 用户
 * 
 * @author cxj
 *
 */
public class User {

	private String name;
	private String pass;

	public User(String name, String pass) {
		this.name = name;
		this.pass = pass;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPass() {
		return pass;
	}

	public void setPass(String pass) {
		this.pass = pass;
	}

	/**
	 * 只有当密码和名字一样的时候才相等
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof User) {
			User u = (User) obj;
			if (name != null && pass != null) {
				return name.equals(u.name);
			}
		}
		return false;
	}

}

博主使用同一个用户名和密码连续注册两次的效果如下

效果图



可以看到第一次是提示成功,第二次是提示用户名存在,并且在控制台打印返回的结果可以看到是json数据,而我们提示成功说明了json数据被成功的转化


Post提交文件

首先是单文件上传,并且提交两个普通的键值对

先添加请求的接口中的上传单个文件的方法

   /**
     * 测试提交一个文件和两个普通的键值对
     *
     * @param fileBody
     * @param nameBody
     * @param passBody
     * @return
     */
    @Multipart
    @POST("test/uploadFile")
    public Call<String> postFile(@Part("file\"; filename=\"avatar.db") RequestBody fileBody, @Part("name") RequestBody nameBody, @Part("pass") RequestBody passBody);

比上述两个稍微复杂一点,因为文件上传必须使用POST请求,而且是一个请求体中分多个部分,所以这里使用@Part表示一部分的请求体,第一个是我们的文件部分的数据,@Part里面的字符串当作这部分的名称,但是我们可以看到我们这里写的比较奇怪,那就先看一下这个请求提交的时候拦截下来的POST报文


这部分是POST请求中最上面的,我们看到倒数第四行中发现了我们所写的内容,原来是为了既能输出name的value值,也能输出这分布的文件的名称,这也就解释的通上述第一个参数中的注解里面的内容了


还有另外两个键值对,也看一下报文吧


乱码的那几个字其实就是我提交的name的值 "小金子"

下面还有一个 '123' 是我提交的pass的值,而我们可以看到我们在请求接口中的@Part注解内的内容作为了这里的name属性的value值


上述查看报文完全是为了帮助读者了解为什么文件部分的那个RequestBody参数的注解@Part为什么是

file\"; filename=\"avatar.db

完全是为了在报文中输出文件名,这里我觉得官方网站还是可以再优化一下的,毕竟这样子让人不太好理解

Activity中上传文件的代码

    public void postFile(View v) {
        //需要上传的文件
        File f = new File(Environment.getExternalStorageDirectory(), "address.db");

        //创建文件部分的请求体对象
        RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), f);

        //普通键值对的请求体
        RequestBody nameBody = RequestBody.create(null,"小金子");
        RequestBody passBody = RequestBody.create(null,"123");

        Call<String> call = MyApp.netWorkService.postFile(fileBody, nameBody, passBody);

        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

                System.out.println("上传成功" + response.body() + response.code() + response.errorBody());

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                System.out.println("上传文件失败");
            }
        });

    }

查看提交的效果


可以看到在服务器上已经收到啦


Post提交多文件

这回我们换一种方法来声明请求接口中的方法


@POST("test/uploadFiles")
public Call<String> postFiles(@Body MultipartBody multipartBody );

这回直接让请求体作为参数,其实这个和自己利用okhttp发送已经很像了

Activity中上传多文件的代码

    /**
     * 多文件上传
     *
     * @param view
     */
    public void postFiles(View view) {

        //需要上传的文件
        File f1 = new File(Environment.getExternalStorageDirectory(), "address.db");
        File f2 = new File(Environment.getExternalStorageDirectory(), "1.apk");

        //创建文件部分的请求体对象
        RequestBody fileBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), f1);
        RequestBody fileBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), f2);

        MultipartBody multipartBody = new MultipartBody.Builder()
                .addFormDataPart("files", f1.getName(), fileBody1)
                .addFormDataPart("files", f2.getName(), fileBody2)
                .addFormDataPart("name", "小金子")
                .addFormDataPart("pass", "123")
                .build();

        Call<String> call = MyApp.netWorkService.postFiles(multipartBody);

        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

                System.out.println("上传成功");

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                System.out.println("上传多文件失败");
            }
        });

    }
由于博主使用的是模拟器,没有那么多的文件测试,这里就用了两个文件,和n个文件原理是一样的,你们可以适度的封装一下,利用for循环来添加文件部分的请求体

服务器上的效果


提交成功啦

总结

其实在我个人看来retrofit只是对okhttp很好的进行了封装,让我们用起来得心应手,但是我强烈建议,在这个框架会使用的同时,也要适当的学习okhttp,对你是很有帮助的,如果对okhttp感兴趣的,请移步我的另一个篇博客

demo和服务器的测试项目代码下载地址






备用地址
访问密码 009d

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值