Android Rxjava2+Retrofit2 網(wǎng)絡(luò)請(qǐng)求封裝

首先列一下本篇文章要實(shí)現(xiàn)的功能
1.對(duì)服務(wù)器返回的數(shù)據(jù)進(jìn)行預(yù)處理
2.請(qǐng)求時(shí)帶上加載對(duì)話框医舆,對(duì)話框取消同時(shí)取消請(qǐng)求

一下以個(gè)人公司返回?cái)?shù)據(jù)為例,先上返回實(shí)體:

返回結(jié)果集基類(lèi)
返回分頁(yè)結(jié)果集基類(lèi)

這里加一句如果返回分頁(yè)數(shù)據(jù)也是要被BaseModel包裹的 BaseModel<PageModel<T>> T可以是所有對(duì)象這個(gè)你們應(yīng)該懂得,分頁(yè)的話T一般是List<你的對(duì)象>夭拌。

接下來(lái)舉個(gè)例子
1.創(chuàng)建Retrofit 數(shù)據(jù)請(qǐng)求類(lèi)

public class SystemApi {
    //超時(shí)時(shí)間
    public static final int DEFAULT_TIMEOUT = 10;
    private static Retrofit provideRetrofit() {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BuildConfig.SERVER_HOST)//服務(wù)器項(xiàng)目地址
                .addConverterFactory(JsonConverterFactory.create())//數(shù)據(jù)轉(zhuǎn)換器饺蚊,重寫(xiě)了下面有代碼
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(client)
                .build();
        return retrofit;
    }

    public static SystemApiService provideService() {
        return provideRetrofit().create(SystemApiService.class);
    }
}

2.數(shù)據(jù)轉(zhuǎn)換器重寫(xiě) 實(shí)現(xiàn)指哪打哪的效果 我要什么結(jié)果就什么結(jié)果 不給就報(bào)錯(cuò)去吧!H病狞换!自帶的不滿足我,具體什么原因忘記了,反正就重寫(xiě)了舟肉。

public class JsonConverterFactory extends Converter.Factory {

    private final Gson gson;

    private JsonConverterFactory(Gson gson) {
        if (gson == null)
            throw new NullPointerException("gson == null");
        this.gson = gson;
    }

    public static JsonConverterFactory create() {
        return create(new Gson());
    }

    public static JsonConverterFactory create(Gson gson) {
        return new JsonConverterFactory(gson);
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonResponseBodyConverter<>(gson, adapter);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonRequestBodyConverter<>(gson, adapter);
    }
}

接下來(lái)實(shí)現(xiàn) response和request轉(zhuǎn)換

final class JsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
    private static final Charset UTF_8 = Charset.forName("UTF-8");

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    JsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public RequestBody convert(T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
        JsonWriter jsonWriter = gson.newJsonWriter(writer);
        adapter.write(jsonWriter, value);
        jsonWriter.close();
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
    }
}
public final class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final Gson gson;
    private final TypeAdapter<T> adapter;
    public final static String JSON_ERROR_STR = "數(shù)據(jù)結(jié)構(gòu)異常!";


    JsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        if (BuildConfig.DEBUG) {
            LogUtil.i("NetData", response);
        }
        try {
            InputStream inputStream = new ByteArrayInputStream(response.getBytes());
            Reader reader = new InputStreamReader(inputStream);
            JsonReader jsonReader = gson.newJsonReader(reader);
            T t = adapter.read(jsonReader);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(JSON_ERROR_STR);
        } finally {
            value.close();
        }


    }
}

到這里底層的數(shù)據(jù)請(qǐng)求轉(zhuǎn)換就完成了修噪,接下來(lái)我們用 RxJava2 方式去請(qǐng)求一個(gè)接口

public interface SystemApiService {

    //去服務(wù)器驗(yàn)證賬號(hào)是否存在,這里就不j介紹更多返回結(jié)果了路媚,反正如果是登陸就返回Observable<BaseModel<UserInfo>>黄琼,
    //如果是獲取用戶(hù)列表分頁(yè)就是Observable<PageModel<List<UserInfo>>>
    @FormUrlEncoded
    @POST("authUserName")
    Observable<BaseModel<Boolean>> authUserName(@FieldMap Map<String, Object> params);

}

接下來(lái)關(guān)鍵代碼來(lái)了,結(jié)果預(yù)處理整慎,不多少說(shuō)直接上代碼脏款,我這前面不寫(xiě)文章的围苫,寫(xiě)了你們也看不懂。

public class RxHelper {
    /**
     * 對(duì)結(jié)果進(jìn)行預(yù)處理
     *
     * @param <T>
     * @return
     */
    public static final <T> ObservableTransformer<BaseModel<T>, T> applySchedulers() {
        return new ObservableTransformer<BaseModel<T>, T>() {
            @Override
            public ObservableSource apply(Observable upstream) {
                return upstream.flatMap(new Function<BaseModel<T>, Observable<T>>() {
                    @Override
                    public Observable<T> apply(BaseModel<T> result) {
                        if (result.ifSuccess()) {//檢查是否掉接口成功了
                            return createData(result.getData());//成功撤师,剝?nèi)∥覀円臄?shù)據(jù)剂府,把BaseModel丟掉
                        } else {
                            return Observable.error(new Exception(result.getMsg()));//出錯(cuò)就返回服務(wù)器錯(cuò)誤
                        }
                    }
                }).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

    /**
     * 創(chuàng)建成功的數(shù)據(jù)
     *
     * @param data
     * @param <T>
     * @return
     */
    private static <T> Observable<T> createData(final T data) {
        return Observable.create(new ObservableOnSubscribe<T>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<T> subscriber) throws Exception {
                try {
                    subscriber.onNext(data);
                    subscriber.onComplete();
                } catch (Exception e) {
                    subscriber.onError(e);
                }
            }
        });
    }
}

預(yù)處理就這么完了,我語(yǔ)言組織能力幾乎為0剃盾,代碼總看的來(lái)吧腺占。
開(kāi)始下一步,對(duì)話框顯示痒谴。怎么弄衰伯,很簡(jiǎn)單繼續(xù)看代碼,要我描述做不到积蔚,最多代碼給你們稍微注釋下

//實(shí)現(xiàn)Observer類(lèi)嚎研,把里面用不到的方法這里全處理掉 我們只處理結(jié)果 成功/失敗
public abstract class RxSubscribe<T> implements Observer<T> {
    private Context mContext;
    private ProgressDialog dialog;
    private String msg;
    private boolean isShow = false;

    public RxSubscribe(Context context) {
        this.mContext = context;
    }

    public RxSubscribe(Context context, boolean isShow) {
        this.mContext = context;
        this.isShow = isShow;
        msg = mContext.getString(R.string.dialog_message_loading);
    }


    public RxSubscribe(Context context, int msgResId) {
        this.mContext = context;
        isShow = true;
        msg = mContext.getString(msgResId);
    }


    @Override
    public void onComplete() {
        if (isShow) {
            if (dialog != null)
                dialog.dismiss();
        }
    }

    @Override
    public void onSubscribe(@NonNull final Disposable d) {
        if (isShow) {
            dialog = new ProgressDialog(mContext);
            dialog.setMessage(msg);
            dialog.setIndeterminate(true);
            dialog.setCanceledOnTouchOutside(false);
            dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialogInterface) {
                 // 對(duì)話框取消 直接停止執(zhí)行請(qǐng)求
                    if (!d.isDisposed()) {
                        d.dispose();
                    }
                }
            });
            dialog.show();

        }
    }

    @Override
    public void onNext(T t) {
        _onNext(t);
    }

    @Override
    public void onError(Throwable e) {
        Log.i("onError",e.getMessage() );
       //把底層的一些錯(cuò)誤翻譯一下用戶(hù)看不來(lái)
        if (e.getMessage() == null || e.getMessage().isEmpty()) {//未知錯(cuò)誤
            _onError(mContext.getString(R.string.error_unknown));
        } else if (e.getMessage().contains("Failed to connect to") ||
                e.getMessage().contains("failed to connect to")) {//連接服務(wù)器失敗
            if (!SystemUtil.isNetworkAvailable(mContext)) {//沒(méi)網(wǎng)絡(luò)
                _onError(mContext.getString(R.string.error_bad_network));
            } else {//真的沒(méi)連接上服務(wù)器
                _onError(mContext.getString(R.string.error_connect_server));
            }
        } else if (e.getMessage().contains("timed out") || e.getMessage().contains("timeout")) {
          //超時(shí)了
            _onError(mContext.getString(R.string.error_connect_server_timeout));
        }else if (e.getMessage().contains("500")) {
            //服務(wù)器內(nèi)部錯(cuò)誤
            _onError(mContext.getString(R.string.error_connect_server_timeout));
        } else {
            _onError(e.getMessage());
        }
        if (isShow) {
            if (dialog != null)
                dialog.dismiss();
        }
    }
    //成功  
    protected abstract void _onNext(T t);
    //失敗
    protected abstract void _onError(String message);
}

基礎(chǔ)的處理全部完成 最后我們的代碼就是請(qǐng)求接口好了

/**
 * Created by 王汗三
 * Date 2017/7/14.
 */

public class RegisterPresenter extends BasePresenter {
    private RegisterActivity activity;

    public RegisterPresenter(RegisterActivity activity) {
        super(activity);
        this.activity = activity;
    }


    public void authUserName(String username) {
        params.put("username", username);
        SystemApi.provideService().authUserName(params).compose(RxHelper.<Boolean>applySchedulers()).subscribe(new RxSubscribe<Boolean>(activity, R.string.dialog_message_auth) {
            @Override
            protected void _onNext(Boolean isExits) {
                if (isExits) {
                    IToast.show(context, "用戶(hù)名已被別家熊孩子搶走了!");
                } else {
                    activity.gotoNext();
                }
            }

            @Override
            protected void _onError(String message) {
                IToast.show(context, message);
            }
        });
    }

}

好了差不多完成了库倘,最后給大家附帶給上傳文件的 看?? 不多說(shuō)

    //上傳文件加參數(shù)
    @Multipart
    @POST("PostThreads")
    Observable<BaseModel<String>> uploadPost(@Part List<MultipartBody.Part> parts);

在帶上個(gè)工具類(lèi)

public class MultipartBodyUtil {

    public static List<MultipartBody.Part> files2Parts(Map<String, String> fileMaps,
                                                       MediaType imageType) {
        List<MultipartBody.Part> parts = new ArrayList<>();
        for (Map.Entry<String, String> entry : fileMaps.entrySet()) {
            File file = new File(entry.getValue());
            RequestBody requestBody = RequestBody.create(imageType, file);
            // 將RequestBody封裝成MultipartBody.Part類(lèi)型(同樣是okhttp的)
            MultipartBody.Part part = MultipartBody.Part.
                    createFormData(entry.getKey(), file.getName(), requestBody);
            // 添加進(jìn)集合
            parts.add(part);
        }
        return parts;
    }



    /**
     * 直接添加文本類(lèi)型的Part到的MultipartBody的Part集合中
     *
     * @param parts Part集合
     * @param key   參數(shù)名(name屬性)
     * @param value 文本內(nèi)容
     */
    public static void addTextPart(List<MultipartBody.Part> parts,
                                   String key, String value) {
            if(value!=null) {
                RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), value);
                MultipartBody.Part part = MultipartBody.Part.createFormData(key, null, requestBody);
                parts.add(part);
            }
    }
}

最后請(qǐng)求

/**
 * Created by 王汗三
 * Date 2017/2/7.
 */

public class SentPostPresenter extends BasePresenter {
    SentPostActivity activity;

    public SentPostPresenter(SentPostActivity activity) {
        super(activity);
        this.activity = activity;
    }

    public void uploadPost(String travelID, String forumId, String title, List<EditData> editDataList) {
        Map<String, String> fileMaps = new HashMap<>();
        for (int i = 0; i < editDataList.size(); i++) {
            if (editDataList.get(i).getImagePath() != null && !editDataList.get(i).getImagePath().isEmpty()) {
                fileMaps.put("file" + (i + 1), editDataList.get(i).getImagePath());
            }
        }
        List<MultipartBody.Part> parts = MultipartBodyUtil.files2Parts(fileMaps, MediaType.parse("image/*;charset=utf-8"));
        addBasePart(parts);//這個(gè)是我們項(xiàng)目必傳參數(shù) 你們可以不用管
        MultipartBodyUtil.addTextPart(parts, "eventId", travelID);
        MultipartBodyUtil.addTextPart(parts, "forumId", forumId);
        MultipartBodyUtil.addTextPart(parts, "title", title);
        String content=new JsonResolver<List<EditData>>() {
        }.toJson(editDataList);
        MultipartBodyUtil.addTextPart(parts, "content", content);
        SystemApi.provideService().uploadPost(parts).compose(RxHelper.<String>handleResult()).subscribe(new RxSubscribe<String>(activity, R.string.dialog_message_submit) {
            @Override
            protected void _onNext(String s) {
                IToast.show(activity, "發(fā)表成功");
                activity.setResult(Activity.RESULT_OK);
                activity.finish();
            }

            @Override
            protected void _onError(String message) {
                Log.i("_onError",message);
                IToast.show(activity, message);
            }
        });

    }
}

這篇文章就到這里 別人說(shuō)授人以魚(yú)不如授人以漁临扮,但是我只會(huì)授魚(yú),吃著魚(yú)領(lǐng)悟吧教翩! 看不懂去看看Rxjava和泛型基礎(chǔ)杆勇。
本文引用第三方庫(kù):
compile 'io.reactivex.rxjava2:rxjava:2.0.7'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.okio:okio:1.13.0'
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.google.code.gson:gson:2.8.0'

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饱亿,隨后出現(xiàn)的幾起案子蚜退,更是在濱河造成了極大的恐慌,老刑警劉巖彪笼,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钻注,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡配猫,警方通過(guò)查閱死者的電腦和手機(jī)幅恋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泵肄,“玉大人捆交,你說(shuō)我怎么就攤上這事「玻” “怎么了品追?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冯丙。 經(jīng)常有香客問(wèn)我肉瓦,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任泞莉,我火速辦了婚禮洁墙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戒财。我一直安慰自己热监,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布饮寞。 她就那樣靜靜地躺著孝扛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幽崩。 梳的紋絲不亂的頭發(fā)上苦始,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音慌申,去河邊找鬼陌选。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蹄溉,可吹牛的內(nèi)容都是我干的咨油。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柒爵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼役电!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起棉胀,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤法瑟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后唁奢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體霎挟,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年麻掸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酥夭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡论笔,死狀恐怖采郎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狂魔,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布淫痰,位于F島的核電站最楷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜籽孙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一烈评、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧犯建,春花似錦讲冠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至玻熙,卻和暖如春否彩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗦随。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工列荔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枚尼。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓贴浙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親署恍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悬而,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 一.榜單介紹 排行榜包括四大類(lèi): 單一框架:僅提供路由、網(wǎng)絡(luò)層锭汛、UI層笨奠、通信層或其他單一功能的框架 混合開(kāi)發(fā)框架:...
    偉子男閱讀 5,243評(píng)論 0 161
  • 附上原文作者連接:作者:金誠(chéng) 一.榜單介紹 排行榜包括四大類(lèi): 單一框架:僅提供路由、網(wǎng)絡(luò)層唤殴、UI層般婆、通信層或其他...
    這個(gè)美嘉不姓陳閱讀 2,261評(píng)論 1 35
  • 忽然發(fā)現(xiàn)插畫(huà)也很有趣,昨天看了別人的分享朵逝,說(shuō)插畫(huà)其實(shí)人人都可以畫(huà)蔚袍。可以畫(huà)一個(gè)故事配名,一段回憶啤咽。一個(gè)場(chǎng)景。渠脉。宇整。。 那么...
    boboooo不閱讀 332評(píng)論 0 0
  • 再昂貴的衣服也無(wú)法幫你解決職場(chǎng)困境芋膘, 隨波逐流鳞青, 只會(huì)讓一個(gè)人距離優(yōu)秀越來(lái)越遠(yuǎn)霸饲。 那個(gè)包裹在昂貴衣飾背后的自己, ...
    與你北郊閱讀 831評(píng)論 0 0