retrofit 使用完全解析

前言

上一篇文章博主介绍了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‘

这个依赖我是查询得到的,博主暂时也不知道哪里有完整的信息

快速搭建环境

编写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布局文件

<span style="font-size:14px;"><?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></span>

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

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报文

123这部分是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感兴趣的,请移步我的另一个篇博客

http://blog.csdn.net/u011692041/article/details/51620388

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

demo:https://github.com/xiaojinzi123/android-demos/tree/master/RetrofitDemo

服务器的:http://download.csdn.net/detail/u011692041/9545805

备用地址

https://yunpan.cn/cRQJyDihiXaWq

访问密码 009d

时间: 2024-10-16 20:50:20

retrofit 使用完全解析的相关文章

Retrofit源码解析

square公司开源了一系列的优秀库,比如Retrofit,OkHttp,Picasso等, 前面简单分析了Picasso的源码,这里来分析下Retrofit的使用: 一.gradle添加依赖 compile 'com.squareup.okhttp:okhttp:2.4.0' compile 'com.squareup.okhttp:okhttp-urlconnection:2.4.0' compile 'com.squareup.okio:okio:1.5.0' compile 'com.g

带你一步步剖析Retrofit 源码解析:一款基于 OkHttp 实现的网络请求框架

OkHttp与Retrofit 的区别与联系是怎样的? 参考答案:OkHttp和Retrofit都是目前流行网络开源框架 封装不同:Retrofit封装了具体的请求,线程切换以及数据转换.retrofit通过使用代理,外观,策略模式对okhttp进行了封装OkHttp 是基于Http协议封装的一套请求客户端 职责不同:Retrofit主要负责应用层面的封装,面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等.OkHttp主要负责socket部分的优化与封装,比如网络访问,多路复用,

Retrofit源码设计模式解析(上)

Retrofit通过注解的方法标记HTTP请求参数,支持常用HTTP方法,统一返回值解析,支持异步/同步的请求方式,将HTTP请求对象化,参数化.真正执行网络访问的是Okhttp,Okhttp支持HTTP&HTTP2,因此,使用Retrofit可以支持REST.HTTPS及SPDY. 行业内分析Retrofit的使用方法的文章已经比较丰富,这里不再赘述,如想了解这部分内容,请参考如下链接. <用 Retrofit 2 简化 HTTP 请求> <Retrofit 源码解析>

Retrofit

Retrofit 不算是一个网络库,它应该算是封装了 okhttp ,retrofit的最大特点就是解耦,要解耦就需要大量的设计模式,然后为我们提供了一个友好的接口的一个工具库吧. 1.创建Retrofit对象: builder 模式,外观模式(门面模式) 外观模式具有高内聚.低耦合的特性,对外提供简单统一的接口,隐蔽了子系统具体的实现.隔离变化. 在封装某些特定功能时,比如下载module,外观模式是一种很好的设计规范.即下载module与其他module通信时通过DownloadManage

Retrofit2 完全解析 探索与okhttp之间的关系

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/51304204: 本文出自:[张鸿洋的博客] 之前写了个okhttputils的工具类,然后有很多同学询问这个工具类和retrofit什么区别,于是上了下官网,发现其底层对网络的访问默认也是基于okhttp,不过retrofit非常适合于restful url格式的请求,更多使用注解的方式提供功能. 既然这样,我们本篇博文首先研究其所提供的常用的用法: 一般的get.post请

Retrofit 网络访问框架简单使用

1.引入远程依赖:包括okhttp;retrofit2;retrofit的GSON解析器 compile'com.squareup.okhttp3:okhttp:3.2.0' compile'com.squareup.retrofit2:retrofit:2.0.2' compile'com.squareup.retrofit2:converter-gson:2.0.2' 2.初始化okhttpclient(可以设置更多的okhttp参数): OkHttpClient client=new Ok

Retrofit2 源码解析

原文链接:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/ 公司里最近做的项目中网络框架用的就是Retrofit,用的多了以后觉得这个框架真的非常好用,然后抽了点时间debug了一下源码,觉得不光代码写的非常好,而且设计这个框架的思路都非常特别,收获很多,决定记录下来 本文的源码分析基于Retrofit 2.0,和Retrofit 1.0有较大的不同, 本文主要分为几部分:0.Retrofit 是什么,1.Retrofit 怎么用,2

Dagger2 使用初步

Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagger1 由Square公司开发.依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性.Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理.Jake Wharton 在对 Dagger 的介绍中指出,Dagger 即 DAG-er,这里的 DAG 即数据结构中的 DAG——有向无环图(Directed Acyclic Graph).也就是说,Dagger 是一个基于有向无环图结构的依赖注入

可靠的功能测试 - Espresso和Dagger2

欢迎Follow我的GitHub, 关注我的CSDN. 可靠的功能测试, 意味着在任何时候, 获取的测试结果均相同, 这就需要模拟(Mock)数据. 测试框架可以使用Android推荐的Espresso. 模拟数据可以使用Dagger2, 一种依赖注入框架. Dagger2已经成为众多Android开发者的必备工具, 是一个快速的依赖注入框架,由Square开发,并针对Android做了特别优化, 已经被Google进行Fork开发. 不像其他的依赖注入器, Dagger2没有使用反射, 而是使