Android Rxjava2+Retrofit2 网络请求封装

首先列一下本篇文章要实现的功能
1.对服务器返回的数据进行预处理
2.请求时带上加载对话框,对话框取消同时取消请求

一下以个人公司返回数据为例,先上返回实体:

返回结果集基类
返回分页结果集基类

这里加一句如果返回分页数据也是要被BaseModel包裹的 BaseModel<PageModel<T>> T可以是所有对象这个你们应该懂得,分页的话T一般是List<你的对象>。

接下来举个例子
1.创建Retrofit 数据请求类

public class SystemApi {
    //超时时间
    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)//服务器项目地址
                .addConverterFactory(JsonConverterFactory.create())//数据转换器,重写了下面有代码
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(client)
                .build();
        return retrofit;
    }

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

2.数据转换器重写 实现指哪打哪的效果 我要什么结果就什么结果 不给就报错去吧!!!自带的不满足我,具体什么原因忘记了,反正就重写了。

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

接下来实现 response和request转换

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 = "数据结构异常!";


    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();
        }


    }
}

到这里底层的数据请求转换就完成了,接下来我们用 RxJava2 方式去请求一个接口

public interface SystemApiService {

    //去服务器验证账号是否存在,这里就不j介绍更多返回结果了,反正如果是登陆就返回Observable<BaseModel<UserInfo>>,
    //如果是获取用户列表分页就是Observable<PageModel<List<UserInfo>>>
    @FormUrlEncoded
    @POST("authUserName")
    Observable<BaseModel<Boolean>> authUserName(@FieldMap Map<String, Object> params);

}

接下来关键代码来了,结果预处理,不多少说直接上代码,我这前面不写文章的,写了你们也看不懂。

public class RxHelper {
    /**
     * 对结果进行预处理
     *
     * @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());//成功,剥取我们要的数据,把BaseModel丢掉
                        } else {
                            return Observable.error(new Exception(result.getMsg()));//出错就返回服务器错误
                        }
                    }
                }).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

    /**
     * 创建成功的数据
     *
     * @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);
                }
            }
        });
    }
}

预处理就这么完了,我语言组织能力几乎为0,代码总看的来吧。
开始下一步,对话框显示。怎么弄,很简单继续看代码,要我描述做不到,最多代码给你们稍微注释下

//实现Observer类,把里面用不到的方法这里全处理掉 我们只处理结果 成功/失败
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) {
                 // 对话框取消 直接停止执行请求
                    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() );
       //把底层的一些错误翻译一下用户看不来
        if (e.getMessage() == null || e.getMessage().isEmpty()) {//未知错误
            _onError(mContext.getString(R.string.error_unknown));
        } else if (e.getMessage().contains("Failed to connect to") ||
                e.getMessage().contains("failed to connect to")) {//连接服务器失败
            if (!SystemUtil.isNetworkAvailable(mContext)) {//没网络
                _onError(mContext.getString(R.string.error_bad_network));
            } else {//真的没连接上服务器
                _onError(mContext.getString(R.string.error_connect_server));
            }
        } else if (e.getMessage().contains("timed out") || e.getMessage().contains("timeout")) {
          //超时了
            _onError(mContext.getString(R.string.error_connect_server_timeout));
        }else if (e.getMessage().contains("500")) {
            //服务器内部错误
            _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);
}

基础的处理全部完成 最后我们的代码就是请求接口好了

/**
 * 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, "用户名已被别家熊孩子抢走了!");
                } else {
                    activity.gotoNext();
                }
            }

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

}

好了差不多完成了,最后给大家附带给上传文件的 看🐴 不多说

    //上传文件加参数
    @Multipart
    @POST("PostThreads")
    Observable<BaseModel<String>> uploadPost(@Part List<MultipartBody.Part> parts);

在带上个工具类

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类型(同样是okhttp的)
            MultipartBody.Part part = MultipartBody.Part.
                    createFormData(entry.getKey(), file.getName(), requestBody);
            // 添加进集合
            parts.add(part);
        }
        return parts;
    }



    /**
     * 直接添加文本类型的Part到的MultipartBody的Part集合中
     *
     * @param parts Part集合
     * @param key   参数名(name属性)
     * @param value 文本内容
     */
    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);
            }
    }
}

最后请求

/**
 * 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);//这个是我们项目必传参数 你们可以不用管
        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, "发表成功");
                activity.setResult(Activity.RESULT_OK);
                activity.finish();
            }

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

    }
}

这篇文章就到这里 别人说授人以鱼不如授人以渔,但是我只会授鱼,吃着鱼领悟吧! 看不懂去看看Rxjava和泛型基础。
本文引用第三方库:
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'

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,280评论 25 707
  • 一.榜单介绍 排行榜包括四大类: 单一框架:仅提供路由、网络层、UI层、通信层或其他单一功能的框架 混合开发框架:...
    伟子男阅读 5,237评论 0 161
  • 附上原文作者连接:作者:金诚 一.榜单介绍 排行榜包括四大类: 单一框架:仅提供路由、网络层、UI层、通信层或其他...
    这个美嘉不姓陈阅读 2,247评论 1 35
  • 忽然发现插画也很有趣,昨天看了别人的分享,说插画其实人人都可以画。可以画一个故事,一段回忆。一个场景。。。。 那么...
    boboooo不阅读 327评论 0 0
  • 再昂贵的衣服也无法帮你解决职场困境, 随波逐流, 只会让一个人距离优秀越来越远。 那个包裹在昂贵衣饰背后的自己, ...
    与你北郊阅读 827评论 0 0