Android中用Retrofit+Rxjava搭建網(wǎng)絡(luò)請(qǐng)求

image.png

前言


最近鳖悠,有不少小伙伴都在問Retrofit怎么用榜掌,近期筆者的幾個(gè)項(xiàng)目上都用到了這個(gè)框架,所以乘综,為了防止宇宙被破壞憎账,為了維護(hù)世界的和平(認(rèn)真臉)!我決定卡辰,還是寫一篇關(guān)于Retrofit的使用說明吧胞皱。
準(zhǔn)備工作

工欲善其事必先利其器,要想使用Retrofit九妈,當(dāng)然首先你得在你的項(xiàng)目里面添加相關(guān)的依賴反砌,下面是筆者項(xiàng)目里添加的依賴:


dependencies{

    compile'com.android.support:appcompat-v7:23.4.0' // V7包支持
    compile'com.squareup.retrofit2:retrofit:2.0.2' // 這個(gè)是Retrofit的依賴
    compile'com.squareup.retrofit2:converter-gson:2.0.2' // 如果要是用Gson轉(zhuǎn)換的話,需要添加這個(gè)依賴
    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
}

這是筆者使用的版本萌朱,當(dāng)然宴树,你也可以直接到Github中查找他的最新版本,這里貼上Github地址:
https://github.com/square/retrofit
https://github.com/ReactiveX/RxJava

請(qǐng)求封裝


一個(gè)網(wǎng)絡(luò)框架接入后嚷兔,當(dāng)然不能直接就這么用了森渐,一點(diǎn)簡(jiǎn)單的封裝是必要的做入。首先,當(dāng)然是單例模式同衣,然后就是相關(guān)的屬性設(shè)置竟块,不多說,上代碼:
RXClientGenerator.java

public class RXClientGenerator {

    private static volatile RXApi rxApi;

    private static Retrofit retrofit;

    private String getBaseUrl() {
        return "http://www.weather.com.cn/"; // 服務(wù)器地址耐齐,筆者寫例子用的是一個(gè)天氣的接口
    }

    private RXClientGenerator() {

        // Retrofit是對(duì)OkHttp的封裝浪秘,所以,在初始化之前埠况,要首先初始化OkHttp
        OkHttpClient client = new OkHttpClient.Builder()
                                .addInterceptor(new NetInterceptor()) // 這行代碼添加了一個(gè)攔截器耸携,我們后面說
                                .build();

        // 好了,這里開始初始化Retrofit
        retrofit = new Retrofit.Builder()
                        .baseUrl(getBaseUrl()) // 這里設(shè)置了服務(wù)器的地址辕翰,可以解釋為所有網(wǎng)絡(luò)請(qǐng)求地址前面的公共部分
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 你要使用RxJava嗎夺衍?加上這個(gè)吧
                        .addConverterFactory(GsonConverterFactory.create()) // 如果你的項(xiàng)目中返回的數(shù)據(jù)是json格式,那么可以添加這行代碼喜命,在獲取服務(wù)器返回?cái)?shù)據(jù)的時(shí)候直接轉(zhuǎn)換成相關(guān)的Bean對(duì)象
                        .client(client) // 把之前初始化好的OkHttp對(duì)象添加進(jìn)去
                        .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;
    }
}

接下來就是網(wǎng)絡(luò)請(qǐng)求接口的定義沟沙,直接上代碼

RXApi.java

public interface RXApi {

    /**
    * 獲取天氣數(shù)據(jù)
    */
    @GET("data/sk/101010100.html") // 這里是接口的地址,會(huì)直接拼接到之前的baseUrl后面
    Observable getWeather(); // 尖括號(hào)中的泛型是返回?cái)?shù)據(jù)的類型

    /**
    * 獲取天氣數(shù)據(jù)
    */
    @GET("data/sk/101010100.html")
    Observable getWeather2(@Query("uid") String uid, // 參數(shù)是基本類型時(shí)壁榕,使用@Query注解矛紫,括號(hào)中是參數(shù)名
                                            @Body WeatherBean body // 參數(shù)是對(duì)象時(shí),使用@Body注解
    );
}

數(shù)據(jù)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;
        }
    }
}

就這樣牌里,對(duì)于Retrofit的簡(jiǎn)單封裝就完成了

Retrofit的使用


對(duì)Retrofit的簡(jiǎn)單封裝后颊咬,就可以在你的項(xiàng)目中使用它進(jìn)行網(wǎng)絡(luò)請(qǐng)求了。

RXClientGenerator.getInstance().createClient() // 單例模式獲取請(qǐng)求對(duì)象
                .getWeather() // 定義在RXApi中的接口方法
                .subscribeOn(Schedulers.io()) // RxJava方法牡辽,控制請(qǐng)求執(zhí)行的線程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1() { // 請(qǐng)求成功回調(diào)
                    @Override
                    public void call(WeatherBean weatherBean) {
                        onSuccess(weatherBean);
                    }
            }, new Action1() { // 請(qǐng)求失敗回調(diào)
                @Override
                public void call(Throwable throwable) {
                    onFail(throwble);
                }
            });

當(dāng)然啦喳篇,你可能會(huì)覺得這還不夠簡(jiǎn)潔,那么我們可以加入JDK1.8的Lambda表達(dá)式來簡(jiǎn)化代碼的書寫态辛。

在Android Studio中使用Lambda表達(dá)式杭隙,需要JDK版本在1.8以上,并且在在Module下的build.gradle中進(jìn)行相關(guān)配置

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

這樣因妙,就可以在代碼中使用Lambda表達(dá)式了,實(shí)現(xiàn)的效果如下:

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

Interceptor


在前面票髓,我們提到了一個(gè)NetInterceptor攀涵,這個(gè)類是一個(gè)攔截器,用來簡(jiǎn)化網(wǎng)絡(luò)請(qǐng)求洽沟,并且可以打印出網(wǎng)絡(luò)請(qǐng)求的詳細(xì)信息以故,方便調(diào)試,不多說裆操,看代碼:

NetInterceptor.java

class NetInterceptor implements Interceptor { // 實(shí)現(xiàn)了OkHttp的Interceptor接口

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

    /**
    * 構(gòu)造方法
    */
    NetInterceptor() {

        versionName = BuildConfig.VERSION_NAME; // 版本名
        platform = "android"; // 應(yīng)用平臺(tái)
        imei = ""; 設(shè)備IMEI
    }

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

        Request oldRequest = chain.request(); // 在這里獲取請(qǐng)求對(duì)象

        // 添加新的參數(shù)
        HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
                                .newBuilder()
                                .scheme(oldRequest.url().scheme())
                                .host(oldRequest.url().host())
                                .addQueryParameter("version", versionName) // 這些是每個(gè)接口都必須有的請(qǐng)求參數(shù)怒详,可以在這里添加
                                .addQueryParameter("platform", platform)
                                .addQueryParameter("imei", imei);

        // 新的請(qǐng)求

        Request request = oldRequest.newBuilder() // 添加完參數(shù)后炉媒,轉(zhuǎn)換成新的請(qǐng)求
                                .method(oldRequest.method(), oldRequest.body())
                                .url(authorizedUrlBuilder.build())
                                .build();

        if (!BuildConfig.DEBUG) // 如果是正式環(huán)境,直接發(fā)送請(qǐng)求并返回
            return chain.proceed(request);

        // 獲取請(qǐng)求數(shù)據(jù)
        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昆烁,將所有數(shù)據(jù)拼接起來
        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();

        // 注意吊骤,這里為了打印出返回的數(shù)據(jù),實(shí)際上是進(jìn)行了一次請(qǐng)求静尼,在開發(fā)中有的接口重復(fù)調(diào)用會(huì)出現(xiàn)數(shù)據(jù)重復(fù)問題白粉,注意判斷
        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); // 發(fā)送請(qǐng)求并返回
    }

    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";
        }
    }
}

怎么樣,是不是又新學(xué)到了一招鼠渺,今天就講到這里了鸭巴,歡迎大家指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拦盹,一起剝皮案震驚了整個(gè)濱河市鹃祖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌普舆,老刑警劉巖恬口,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奔害,居然都是意外死亡楷兽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門华临,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芯杀,“玉大人,你說我怎么就攤上這事雅潭〗液瘢” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵扶供,是天一觀的道長(zhǎng)筛圆。 經(jīng)常有香客問我,道長(zhǎng)椿浓,這世上最難降的妖魔是什么太援? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮扳碍,結(jié)果婚禮上提岔,老公的妹妹穿的比我還像新娘。我一直安慰自己笋敞,他們只是感情好碱蒙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般赛惩。 火紅的嫁衣襯著肌膚如雪哀墓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天喷兼,我揣著相機(jī)與錄音篮绰,去河邊找鬼。 笑死褒搔,一個(gè)胖子當(dāng)著我的面吹牛阶牍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播星瘾,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼走孽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了琳状?” 一聲冷哼從身側(cè)響起磕瓷,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎念逞,沒想到半個(gè)月后困食,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翎承,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年硕盹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叨咖。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘩例,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甸各,到底是詐尸還是另有隱情垛贤,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布趣倾,位于F島的核電站聘惦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏儒恋。R本人自食惡果不足惜善绎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诫尽。 院中可真熱鬧涂邀,春花似錦、人聲如沸箱锐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驹止。三九已至浩聋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臊恋,已是汗流浹背衣洁。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抖仅,地道東北人坊夫。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像撤卢,于是被迫代替她去往敵國(guó)和親环凿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容