Android中用Retrofit+Rxjava搭建网络请求

image.png

前言


最近,有不少小伙伴都在问Retrofit怎么用,近期笔者的几个项目上都用到了这个框架,所以,为了防止宇宙被破坏,为了维护世界的和平(认真脸)!我决定,还是写一篇关于Retrofit的使用说明吧。
准备工作

工欲善其事必先利其器,要想使用Retrofit,当然首先你得在你的项目里面添加相关的依赖,下面是笔者项目里添加的依赖:


dependencies{

    compile'com.android.support:appcompat-v7:23.4.0' // V7包支持
    compile'com.squareup.retrofit2:retrofit:2.0.2' // 这个是Retrofit的依赖
    compile'com.squareup.retrofit2:converter-gson:2.0.2' // 如果要是用Gson转换的话,需要添加这个依赖
    compile'com.squareup.retrofit2:adapter-rxjava:2.0.2' // 用于Retrofit支持RxJava
    compile'io.reactivex:rxjava:1.1.0' // RxJava
    compile'io.reactivex:rxandroid:1.1.0' // RxAndroid
}

这是笔者使用的版本,当然,你也可以直接到Github中查找他的最新版本,这里贴上Github地址:
https://github.com/square/retrofit
https://github.com/ReactiveX/RxJava

请求封装


一个网络框架接入后,当然不能直接就这么用了,一点简单的封装是必要的。首先,当然是单例模式,然后就是相关的属性设置,不多说,上代码:
RXClientGenerator.java

public class RXClientGenerator {

    private static volatile RXApi rxApi;

    private static Retrofit retrofit;

    private String getBaseUrl() {
        return "http://www.weather.com.cn/"; // 服务器地址,笔者写例子用的是一个天气的接口
    }

    private RXClientGenerator() {

        // Retrofit是对OkHttp的封装,所以,在初始化之前,要首先初始化OkHttp
        OkHttpClient client = new OkHttpClient.Builder()
                                .addInterceptor(new NetInterceptor()) // 这行代码添加了一个拦截器,我们后面说
                                .build();

        // 好了,这里开始初始化Retrofit
        retrofit = new Retrofit.Builder()
                        .baseUrl(getBaseUrl()) // 这里设置了服务器的地址,可以解释为所有网络请求地址前面的公共部分
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 你要使用RxJava吗?加上这个吧
                        .addConverterFactory(GsonConverterFactory.create()) // 如果你的项目中返回的数据是json格式,那么可以添加这行代码,在获取服务器返回数据的时候直接转换成相关的Bean对象
                        .client(client) // 把之前初始化好的OkHttp对象添加进去
                        .build();
    }

    private static class Helper {
        static final RXClientGenerator INSTANCE = new RXClientGenerator();
    }

    public static RXClientGenerator getInstance() {
        return Helper.INSTANCE;
    }

    public synchronized RXApi createClient() {
        if (null == rxApi)
            return rxApi = retrofit.create(RXApi.class);
        else
            return rxApi;
    }
}

接下来就是网络请求接口的定义,直接上代码

RXApi.java

public interface RXApi {

    /**
    * 获取天气数据
    */
    @GET("data/sk/101010100.html") // 这里是接口的地址,会直接拼接到之前的baseUrl后面
    Observable getWeather(); // 尖括号中的泛型是返回数据的类型

    /**
    * 获取天气数据
    */
    @GET("data/sk/101010100.html")
    Observable getWeather2(@Query("uid") String uid, // 参数是基本类型时,使用@Query注解,括号中是参数名
                                            @Body WeatherBean body // 参数是对象时,使用@Body注解
    );
}

数据Bean

WeatherBean.java

public class WeatherBean {

    private WeatherinfoBean weatherinfo;

    public WeatherinfoBean getWeatherinfo() {
        return weatherinfo;
    }

    public void setWeatherinfo(WeatherinfoBean weatherinfo) {
        this.weatherinfo = weatherinfo;
    }

    public static class WeatherinfoBean {

        private String city;
        private String temp1;
        private String temp2;
        private String weather;

        public String getCity() {
            return city;
        }
        public String getTemp1() {
            return temp1;
        }
        public String getTemp2() {
            return temp2;
        }
        public String getWeather() {
            return weather;
        }
    }
}

就这样,对于Retrofit的简单封装就完成了

Retrofit的使用


对Retrofit的简单封装后,就可以在你的项目中使用它进行网络请求了。

RXClientGenerator.getInstance().createClient() // 单例模式获取请求对象
                .getWeather() // 定义在RXApi中的接口方法
                .subscribeOn(Schedulers.io()) // RxJava方法,控制请求执行的线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1() { // 请求成功回调
                    @Override
                    public void call(WeatherBean weatherBean) {
                        onSuccess(weatherBean);
                    }
            }, new Action1() { // 请求失败回调
                @Override
                public void call(Throwable throwable) {
                    onFail(throwble);
                }
            });

当然啦,你可能会觉得这还不够简洁,那么我们可以加入JDK1.8的Lambda表达式来简化代码的书写。

在Android Studio中使用Lambda表达式,需要JDK版本在1.8以上,并且在在Module下的build.gradle中进行相关配置

android {
    ...
    defaultConfig {
        ...
        jackOptions {
            enabled true
        }
    }
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

这样,就可以在代码中使用Lambda表达式了,实现的效果如下:

RXClientGenerator.getInstance().createClient()
                            .getWeather("0")
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(weatherBean -> onSuccess(weatherBean),
                                throwable -> onFail(throwble));

Interceptor


在前面,我们提到了一个NetInterceptor,这个类是一个拦截器,用来简化网络请求,并且可以打印出网络请求的详细信息,方便调试,不多说,看代码:

NetInterceptor.java

class NetInterceptor implements Interceptor { // 实现了OkHttp的Interceptor接口

    private static final Charset UTF8 = Charset.forName("UTF-8");
    private String versionName;
    private String platform;
    private String imei;

    /**
    * 构造方法
    */
    NetInterceptor() {

        versionName = BuildConfig.VERSION_NAME; // 版本名
        platform = "android"; // 应用平台
        imei = ""; 设备IMEI
    }

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request oldRequest = chain.request(); // 在这里获取请求对象

        // 添加新的参数
        HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
                                .newBuilder()
                                .scheme(oldRequest.url().scheme())
                                .host(oldRequest.url().host())
                                .addQueryParameter("version", versionName) // 这些是每个接口都必须有的请求参数,可以在这里添加
                                .addQueryParameter("platform", platform)
                                .addQueryParameter("imei", imei);

        // 新的请求

        Request request = oldRequest.newBuilder() // 添加完参数后,转换成新的请求
                                .method(oldRequest.method(), oldRequest.body())
                                .url(authorizedUrlBuilder.build())
                                .build();

        if (!BuildConfig.DEBUG) // 如果是正式环境,直接发送请求并返回
            return chain.proceed(request);

        // 获取请求数据
        RequestBody requestBody = request.body();
        boolean hasRequestBody = requestBody != null;
        Connection connection = chain.connection();
        Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
        StringBuilder sb = new StringBuilder(); // 使用StringBuilder,将所有数据拼接起来
        sb.append("\n--> ").append(request.method()).append(" ").append(request.url()).append(" ").append(protocol);
        if (hasRequestBody) {
            sb.append(" (").append(requestBody.contentLength()).append("-byte body");
        }

        sb.append("\n");

        if (hasRequestBody) {
            if (requestBody.contentType() != null) {
                sb.append("Content-Type: ").append(requestBody.contentType()).append("\n");
            }
            if (requestBody.contentLength() != -1) {
                sb.append("Content-Length: ").append(requestBody.contentLength()).append("\n");
            }
        }

        Headers headers = request.headers();

        for (int i = 0, count = headers.size(); i < count; i++) {
            String name = headers.name(i);
            if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                sb.append(name).append(": ").append(headers.value(i)).append("\n");
            }
        }

        if (!hasRequestBody) {
            sb.append("--> END ").append(request.method()).append("\n");
        } else if (bodyEncoded(request.headers())) {
            sb.append("--> END ").append(request.method()).append(" (encoded body omitted)").append("\n");
        } else {
            Buffer buffer = new Buffer();
            requestBody.writeTo(buffer);
            Charset charset = UTF8;
            MediaType contentType = requestBody.contentType();
            if (contentType != null) {
                charset = contentType.charset(UTF8);
            }
            sb.append("\n");
            sb.append(buffer.readString(charset)).append("\n");
            sb.append("--> END ").append(request.method()).append(" (").append(requestBody.contentLength()).append("-byte body)").append("\n");
        }

        long startNs = System.nanoTime();

        // 注意,这里为了打印出返回的数据,实际上是进行了一次请求,在开发中有的接口重复调用会出现数据重复问题,注意判断
        Response response = chain.proceed(request);
        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
        ResponseBody responseBody = response.body();
        long contentLength = responseBody.contentLength();
        String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";

        sb.append("<-- ").append(response.code()).append(" ").append(response.message()).append(" ")
            .append(response.request().url()).append(" (").append(tookMs).append("ms, ")
            .append(bodySize).append(" body)").append("\n");

        Headers headers1 = response.headers();
        for (int i = 0, count = headers1.size(); i < count; i++) {
            sb.append(headers1.name(i)).append(": ").append(headers1.value(i)).append("\n");
        }

        if (!HttpEngine.hasBody(response)) {
            sb.append("<-- END HTTP").append("\n");
        } else if (bodyEncoded(response.headers())) {
            sb.append("<-- END HTTP (encoded body omitted)").append("\n");
        } else {
                BufferedSource source = responseBody.source();
                source.request(Long.MAX_VALUE); // Buffer the entire body.
                Buffer buffer = source.buffer();
                Charset charset = UTF8;
                MediaType contentType = responseBody.contentType();
                if (contentType != null) {
                    try {
                        charset = contentType.charset(UTF8);
                    } catch (UnsupportedCharsetException e) {
                        sb.append("\n");
                        sb.append("Couldn't decode the response body; charset is likely malformed.").append("\n");
                        sb.append("<-- END HTTP").append("\n");
                        return response;
                    }
                }

                if (contentLength != 0) {
                    sb.append("\n");

                String json = buffer.clone().readString(charset);
                sb.append(json).append("\n\n");

                String str = jsonFormat(json);
                if (str.length() > 1200) {
                    String start = str.substring(0, 600);
                    String end = str.substring(str.length() - 600);
                    sb.append(start).append("\n")
                        .append("\nThe json was too long...\n\n")
                        .append(end).append("\n");
                } else {
                    sb.append(str).append("\n");
                }
            }

            sb.append("<-- END HTTP (").append(buffer.size()).append("-byte body)").append("\n");
        }

        Log.d("NET_INFO", sb.toString()); // 打印信息

        return chain.proceed(request); // 发送请求并返回
    }

    private boolean bodyEncoded(Headers headers) {

        String contentEncoding = headers.get("Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }

    /**

    * 将json字符串格式化后返回

    *

    * @param json json字符串

    * @return 格式化后的字符串

    */

    private String jsonFormat(String json) {

        if (TextUtils.isEmpty(json)) {
            return "Empty/Null json content";
        }

        try {
            json = json.trim();
            String message;
            if (json.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(json);
                message = jsonObject.toString(2);
                return message;
            } else if (json.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(json);
                message = jsonArray.toString(2);
                return message;
            } else {
                message = "Invalid Json";
            }
            return message;
        } catch (JSONException e) {
            Log.e("JSON_ERROR", e.getMessage());
            return "Invalid Json";
        }
    }
}

怎么样,是不是又新学到了一招,今天就讲到这里了,欢迎大家指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容