一款基于MVP、RxJava2和Retrofit2的Android开发框架

前言

该框架的内容是我在工作中自己积累下来的,现分享给大家。由于毕业后我只身一人负责公司的Android项目,都是一些小型的项目,自身能力也着实有限,如有不妥之处,还望大家指正。

我的GitHub主页:https://github.com/BigWolfDean
本框架的GitHub地址:https://github.com/BigWolfDean/Mvp-Rxjava2-Retrofit2-for-Android


说明

本篇文章主要是为了分享框架,不为教学,所以这里关于MVP、RxJava2以及Retrofit2相关的知识不作讲解,如有这几个方面没有任何基础的同学,推荐以下几个链接供大家学习:

  1. 给 Android 开发者的 RxJava 详解(RxJava1)
  2. 给初学者的RxJava2.0教程(一)
  3. 你真的会用Retrofit2吗?Retrofit2完全教程
  4. Android MVP 详解(上)

以上这些文章都是我看过且学习过的,都是非常值得学习的,没有基础的童鞋们可以先进行学习哦。


项目结构

说了这么多,现在咱们就直奔主题了,先来看一下项目的包结构
这里写图片描述
从图中可以看出,本工程第一层共分了4个包

  • bean 用来存放JavaBean文件,用于Json数据转换
  • mvp 用来存放mvp模式下的内容,包含子包 manager、presenter和view
  • retrofit 用来存放retrofit相关内容
  • rx 用来存放rxJava&RxAndroid相关内容

到这里可能有人会疑惑MVP的结构不应该是Model、View和Presenter吗?为什么这里是manager不是model?
我们先来回顾一下MVP的思想吧。

MVP的基本思想:Presenter负责逻辑的处理,Model提供数据,View负责显示

MVP

DataManager层

Model是用来提供数据的,那我们DataManager是干嘛的呢,我们来看一下代码:

/**
 * Created by lang on 2017/12/8.
 * 数据管理类
 * 用于获取retrofitService的实例及生成并返回对应接口的Observable实例
 */

public class DataManager {


    private RetrofitService retrofitService;

    public DataManager() {
        this.retrofitService = RetrofitFactory.getInstance();
    }

    /**
     * 此处返回 Observable实例,由Presenter层调用
     *
     * @param apiKey
     * @param city
     * @param start
     * @param count
     * @return
     */
    public Observable<InTheaterBean> getMovieInTheater(String apiKey, String city, int start, int count) {
        return retrofitService.getMovieInTheater(apiKey, city, start, count);
    }


}

我们可以看到DataManager中getMovieInTheater方法返回的是Observable实例,那么我们就会发现,这一步操作已然就是对外提供了数据,那么这个DataManager也就是DataModel,这便是传统Model层与RxJava结合的产物。

View层

IBaseView

/**
 * Created by lang on 2017/12/8.
 * View层的基类,可自行根据项目需要在此指定公用的方法
 */

public interface IBaseView {

}

InTheaterMovieView

/**
 * Created by lang on 2018/2/6.
 * 继承自IBaseView
 * 声明View层方法
 */

public interface InTheaterMovieView extends IBaseView {

    //设置从网络上获取到的结果
    void setResponse(String response);

    //设置错误信息 包含系统返回的错误和开发者自定义的错误
    void setError(String error);
}

Presenter层

IPresenter

/**
 * Created by lang on 2017/12/8.
 * 利用IPresenter来管理业务流程
 * onCreate 可用来做初始化操作,如类的实例化等
 * onStart  可用来做初始化后的操作,如变量的赋值等
 * onPause  根据项目需要决定内容
 * onStop   同上
 * attachView   绑定继承自IBaseView的接口,用于view层的展示
 * attachInComingIntent 绑定从外部传进来的Intent,对Intent相关进行操作
 * 推荐使用顺序  onCreate -> onStart -> attachView -> attachingInComingIntent -> onPause -> onStop
 * 注意: 使用的过程中一定要根据自己制定的流程来进行操作,否则位置一乱就会发生各种错误
 */

public interface IPresenter {

    void onCreate();

    void onStart();

    void onPause();

    void onStop();

    // 绑定view
    void attachView(IBaseView view);

    // 绑定intent
    void attachIncomingIntent(Intent intent);
}

InTheaterMoviePresenter

/**
 * Created by lang on 2018/2/6.
 * 实现IPresenter接口
 * 重写对应方法
 */

public class InTheaterMoviePresenter implements IPresenter {

    private InTheaterMovieView movieView;

    private DataManager dataManager;

    @Override
    public void onCreate() {
        dataManager = new DataManager();
    }

    @Override
    public void onStart() {

    }


    @Override
    public void onPause() {

    }

    @Override
    public void onStop() {

    }


    @Override
    public void attachView(IBaseView view) {
        movieView = (InTheaterMovieView) view;
    }

    @Override
    public void attachIncomingIntent(Intent intent) {

    }

    /**
     * 获取豆瓣正在上映的电影信息
     *
     * @param apiKey
     * @param city
     * @param start
     * @param count
     */
    public void getMovieInfo(String apiKey, String city, int start, int count) {
        dataManager.getMovieInTheater(apiKey, city, start, count).compose(RxSchedulers.<InTheaterBean>compose()).subscribe(new BaseObserver<InTheaterBean>() {
            @Override
            protected void onHandleSuccess(InTheaterBean inTheaterBean) {
                // 成功的操作
                movieView.setResponse(inTheaterBean.toString());
            }

            @Override
            protected void onHandleError(Throwable e) {
                // 失败的操作
                movieView.setError(e.getMessage());
            }

        });
    }

    /**
     * 设置错误信息
     *
     * @param errorInfo
     */
    public void setErrorInfo(String errorInfo) {
        movieView.setError(errorInfo);
    }
}

Retrofit

RetrofitFactory

/**
 * Created by lang on 2017/11/21.
 * Retrofit工厂类
 */

public class RetrofitFactory {

    private static final String BASE_URL = "https://api.douban.com/v2/";  // 根路由

    private static final long TIMEOUT = 30;  //超时时间,此处单位为秒

    // Retrofit是基于OkHttpClient的,可以创建一个OkHttpClient进行一些配置
    private static OkHttpClient httpClient = new OkHttpClient.Builder()
            // 添加通用的Header
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(@NonNull Chain chain) throws IOException {
                    //此处可根据项目实际需求自行设置header内容
                    Request.Builder builder = chain.request().newBuilder();
                    builder.addHeader("key", "value");
                    return chain.proceed(builder.build());
                }
            })
            /*
            这里可以添加一个HttpLoggingInterceptor,因为Retrofit封装好了从Http请求到解析,
            出了bug很难找出来问题,添加HttpLoggingInterceptor拦截器方便调试接口
             */
            .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(@NonNull String message) {
                    Log.e("HttpLoggingInterceptor", message);
                }
            }).setLevel(HttpLoggingInterceptor.Level.BODY)) //此处级别有NONE,BASIC,HEADERS和BODY
            .connectTimeout(TIMEOUT, TimeUnit.SECONDS) //连接超时时间,单位秒
            .readTimeout(TIMEOUT, TimeUnit.SECONDS) //连接超时时间,单位秒
            .build();

    private static RetrofitService retrofitService = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            // 添加Gson转换器
            .addConverterFactory(GsonConverterFactory.create(buildGson()))
            // 添加Retrofit到RxJava的转换器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(httpClient)
            .build()
            .create(RetrofitService.class);


    public static RetrofitService getInstance() {
        return retrofitService;
    }

    private static Gson buildGson() {
        return new GsonBuilder()
                .serializeNulls()
                .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
                .create();
    }

}

RetrofitService

/**
 * Created by lang on 2018/02/06.
 * RetrofitService接口
 */

public interface RetrofitService {

    /**
     * @param token 固定值`0b2bdeda43b5688921839c8ecb20399b`
     * @param city  所在城市,例如`北京`、`上海`等
     * @param start 分页使用,表示第几页
     * @param count 分页使用,表示数量
     * @return Observable实例
     */
    @GET("movie/in_theaters")
    Observable<InTheaterBean> getMovieInTheater(@Query("apikey") String token,
                                                @Query("city") String city,
                                                @Query("start") int start,
                                                @Query("count") int count);
}

Rx

BaseObserver

/**
 * Created by lang on 2017/11/21.
 * Observer基类
 */

public abstract class BaseObserver<T> implements Observer<T> {

    private static final String TAG = "BaseObserver";

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(T value) {
        onHandleSuccess(value);
    }

    @Override
    public void onError(Throwable e) {
        onHandleError(e);
        Log.e(TAG, "error:" + e.toString());
    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete");
    }


    protected abstract void onHandleSuccess(T t);

    protected abstract void onHandleError(Throwable e);

}

RxSchedulers

/**
 * Created by lang on 2017/11/21.
 * Rx调度器,指定线程在IO
 */

public class RxSchedulers {
    public static <T> ObservableTransformer<T, T> compose() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> observable) {
                return observable
                        .subscribeOn(Schedulers.io())
                        .doOnSubscribe(new Consumer<Disposable>() {
                            @Override
                            public void accept(Disposable disposable) throws Exception {
                                //这里可自行做些操作,如判断网络连接状态等
                            }
                        })
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

如何关联?

以上所贴出的代码,我也就不多做描述了,结合里面的注释看,只要是有基础的童鞋都能看的明白。那么现在的问题是这些东西是如何关联起来的,这里我贴出部分Activity中的代码,具体的代码可以从GitHub上clone哦~
MainActivity重点代码:

(1)
    private InTheaterMoviePresenter moviePresenter = new InTheaterMoviePresenter();
(2)
    private InTheaterMovieView inTheaterMovieView = new InTheaterMovieView() {
        @Override
        public void setResponse(String response) {
            tvResponse.setText(response);
        }

        @Override
        public void setError(String error) {
            tvResponse.setText(error);
        }
    };

(3)
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        moviePresenter.onCreate();
        moviePresenter.onStart();
        moviePresenter.attachView(inTheaterMovieView);
    }
(4)
@Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_request:
                String city = etCity.getText().toString();
                String countString = etCount.getText().toString();
                String startString = etStart.getText().toString();
                String apiKey = etApiKey.getText().toString();
                if (!TextUtils.isEmpty(apiKey) && !TextUtils.isEmpty(city)
                        && !TextUtils.isEmpty(countString)
                        && !TextUtils.isEmpty(startString)) {
                    moviePresenter.getMovieInfo(apiKey, city, Integer.valueOf(startString), Integer.valueOf(countString));
                } else {
                    moviePresenter.setErrorInfo("输入框不可为空");
                }
                break;
        }
    }

代码中主要截取了四块内容

  1. 实例化InTheaterMoviePresenter
  2. 声明InTheaterMovieView接口,复写setResponse和setError方法。
  3. 在onCreate中调用presenter中的方法,将已声明的view进行绑定
  4. 通过调用presenter中自定义的方法进行相关操作

总结

框架的相关介绍到此也就结束了,具体的内容欢迎大家去GitHub上clone代码,如有不足之处欢迎提Issue,如代码对您有些许的帮助,麻烦帮忙给个Star,谢谢!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值