基于Retrofit2+OkHttp3+RxJava实现的MVP框架(1)

基于项目需要,特别设计了一套简单的MVP开发框架,用以描述基于REST接口协议的业务数据流向-获取到展示的过程。

  • V模块-视图模块,是由Activity、Fragment等窗口组件来实现。
  • P模块-展现模块,是由自定义Presenter类及子类实现。
  • M模块-业务数据模块,则是基于Retrofit2+OkHttp3+RxJava实现。Retrofit2+OkHttp3主要是为了方便快速的业务接口的实现,RxJava是为了解决跨线程的数据交互。

基于以上描述,我们先来介绍一下View和Presenter的定义:

1. View模块

    View的相关类设计包括一个接口IView,一个基类BaseView。

IView

public interface IView {
    public void onActionStart();
    public void onActionSuccess();
    public void onActionFailure();
}

IView定义了一些抽象方法,用来描述View类型应该具备的基本行为(方法),作为与Presenter进行交互的渠道。这里定义的三个方法描述了开始、成功、失败共三种行为,则具体的展示方式则有其实现类完成。

BaseView

public abstract class BaseView extends Activity implements IView {
@Override
    public void onActionStart() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionStart");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action starts...", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onActionSuccess() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionSuccess");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action success!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onActionFailure() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionFailure");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action failure!", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

BaseView实现了IView的三个通用行为方法,以Toast展示成功失败,以ProgressDialog展示等待状态。更多的样式及行为完全自定义。考虑到APP交互设计过程中,特别的网络请求过程,大致行为相同,所以可以抽象一部分在基类中实现,业务模块子类则不用单独实现。

2. Presenter

    Presenter部分也包含一个IPresenter接口,一个BasePresenter基类。

IPresenter

public interface IPresenter {
    void destroy();
}

IPresenter接口,定义了通用行为-destroy。

BasePresenter

public abstract class BasePresenter implements IPresenter {    
    public void destroy() {
        // TODO destroy
    }
}

BasePresenter实现了IPresenter的destroy方法,同时可以增加自己的通用行为。

我们都知道,View和Presenter的交互是双向的,View通过用户动作向Presenter发起指令,Presenter执行完之后,将结果告知View做进一步的展示。那么View和Presenter是如何完成交互,它们的关系是如何建立的呢?

首先,我们定义一个原则:

  • 一个Presenter只能对应一个View,反之亦然

这样定义的考虑是,更加明确清晰具体的业务功能。比如,登录功能,则对应一个LoginView和LoginPresenter。基于这个原则,我们可以通过如下方式,将Presenter与View关联起来,

public abstract class BasePresenter<V extends IView> implements IPresenter {
    protected V mView;

    public BasePresenter(V view) {
        mView = view;
    }

    public void destroy() {
        mView = null;
    }
}

我们对BasePresenter增加范型限制,并且该类型必须是IView的实现类,这样就可以将具体业务的Presenter和View给关联起来。同时,Presenter中包含有一个IView实现类的对象mView,在创建Presenter时传入作为属性,那么在需要告知View该展示何种状态时,则可以通过该View属性,完成消息传递-方法调用。还是以登录作为例子,

public class LoginPresenter extends BasePresenter<LoginPresenter.LoginView> {
    public void login(String username, String password) {
        // TODO
    }
    public interface LoginView extends IView {
        public void onLoginSuccess(String token);
        public void onLogining();
        public void onLoginFailure();
    }
}

此时,LoginPresenter声明了范型LoginView,而LoginView是作为内部接口来定义的,并且继承了IView。这么做就是考虑到,具体的展示策略是由Presenter来规范的,那么将具体业务的View定义在该业务的Presenter中,是很合理的。想要具备此业务功能,则实现该View接口。LoginPresenter对外提供了登录功能的对应方法login(), 入餐为用户名和密码;在完成登录后,LoginPresenter则可以通过LoginView对象,来完成消息传递,告知成功、失败。

上面讲到的是从Presenter到View的关联,那么接下来我们来看View到Presenter的关联。

public abstract class BaseView extends Activity implements IView {
    private IPresenter[] mAllPresenters = null;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // init all presenter will be used
        mAllPresenters = getPresenter();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // destroy all presenters when destroy view
        if (mAllPresenters != null && mAllPresenters.length > 0) {
            for (IPresenter presenter : mAllPresenters) {
                presenter.destroy();
            }
        }
    }
}

这里看到,BaseView中定义了Presenter类型的数组对象,该对象保存着当前所需业务对应的所有Presenter对象。并且在onDestroy方法中全部调用destory。可能有人会疑问,上面讲到了一个View-Presenter的一对一关联原则,这里为何是一对多?

其实刚才讲的一对一,是在具体的单个业务功能的角度来看。View的具体业务实现类定义由Presenter的对应业务实现类来完成定义,这样都对应一个具体功能,站在具体功能业务角度,就是一对一的关系。而这里的代码看起来是一对多的关系,是因为用到了数组,这里是站在代码实现的角度。假设这个BaseView的具体实现类需要仅完成一个功能,那么Presenter数组个数为1,那么就是一对一关系;如果要完成多个功能,那么数组个数就为N,这里它已经集成了多个功能业务,那么这个View既是功能A的View,也是功能B的V,这里已经不是站在一个业务功能上来看待这个View了。BaseView是Activity的子类,但不能够完全对等于MVP模式中的View。

public class LoginActivity extends BaseView implements LoginPresenter.LoginView{
    private LoginPresenter mPresenter = new LoginPresenter(this);
@Override
    protected BasePresenter[] getPresenter() {
        return new BasePresenter[]{mPresenter};
    }
    /*-----------Implements of LoginView-----------*/
    @Override
    public void onLoginSuccess(String token) {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
        Toast.makeText(
                getApplicationContext(), "登录成功 token:" + token, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLogining() {
        mProgressDialog =
                ProgressDialog.show(this, null, "登录中...");
    }

    @Override
    public void onLoginFailure() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
        Toast.makeText(
                getApplicationContext(), "登录失败!", Toast.LENGTH_SHORT).show();
    }
}

上面这段代码,则是以登录的Activity为例,按照上述思想,很容易理解。所以,在实现View和Presenter的时候,分别继承BaseView和BasePresenter来完成具体业务功能开发即可。

至此,V和P的关联关系的设计思路已经说明完毕,下一篇将介绍核心-业务数据层M的设计思路及实现。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的 MVP 框架,包含 Okhttp+Retrofit 网络封装,Base 基类的抽取以及 APPLocation 的代码: 1. 首先创建一个 BaseView 接口,定义一些公共的 UI 操作方法: ```java public interface BaseView { void showLoading(); void hideLoading(); void showError(String error); } ``` 2. 接着创建一个 BasePresenter 类,定义一些公共的 Presenter 操作方法: ```java public class BasePresenter<V extends BaseView> { private WeakReference<V> mViewRef; public void attachView(V view) { mViewRef = new WeakReference<>(view); } public void detachView() { if (mViewRef != null) { mViewRef.clear(); mViewRef = null; } } public boolean isViewAttached() { return mViewRef != null && mViewRef.get() != null; } public V getView() { return mViewRef.get(); } public void checkViewAttached() { if (!isViewAttached()) throw new RuntimeException("Please call attachView() before requesting data to the Presenter"); } } ``` 3. 创建一个 BaseActivity 类,作为所有 Activity 的基类,包含一些公共的操作: ```java public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView { protected P mPresenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); mPresenter = createPresenter(); if (mPresenter != null) { mPresenter.attachView(this); } initView(); } @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); } } protected abstract int getLayoutId(); protected abstract P createPresenter(); protected abstract void initView(); } ``` 4. 接着创建一个 BaseFragment 类,作为所有 Fragment 的基类,也包含一些公共的操作: ```java public abstract class BaseFragment<P extends BasePresenter> extends Fragment implements BaseView { protected P mPresenter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter = createPresenter(); if (mPresenter != null) { mPresenter.attachView(this); } } @Override public void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); } } protected abstract P createPresenter(); } ``` 5. 创建一个 AppApplication 类,作为整个应用程序的入口,包含一些公共的配置信息和初始化操作: ```java public class AppApplication extends Application { private static AppApplication sInstance; @Override public void onCreate() { super.onCreate(); sInstance = this; // 初始化网络请求 RetrofitClient.getInstance().init(this); } public static AppApplication getInstance() { return sInstance; } } ``` 6. 创建一个 RetrofitClient 类,用于封装 Okhttp+Retrofit 网络请求: ```java public class RetrofitClient { private static final String TAG = "RetrofitClient"; private static final int DEFAULT_TIMEOUT = 30; private Retrofit mRetrofit = null; private OkHttpClient mOkHttpClient = null; private RetrofitClient() {} public static RetrofitClient getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final RetrofitClient INSTANCE = new RetrofitClient(); } public void init(Context context) { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); mOkHttpClient = new OkHttpClient.Builder() .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .addInterceptor(loggingInterceptor) .addInterceptor(new TokenInterceptor()) .build(); mRetrofit = new Retrofit.Builder() .baseUrl(ApiService.BASE_URL) .client(mOkHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } public ApiService getApiService() { return mRetrofit.create(ApiService.class); } } ``` 7. 创建一个 ApiService 接口,定义所有的网络请求接口: ```java public interface ApiService { String BASE_URL = "https://www.example.com/"; @POST("login") Observable<BaseResponse<User>> login(@Query("username") String username, @Query("password") String password); } ``` 8. 最后,我们可以创建一个 LoginPresenter 类,来处理登录相关的业务逻辑: ```java public class LoginPresenter extends BasePresenter<LoginContract.View> implements LoginContract.Presenter { private ApiService mApiService; public LoginPresenter() { mApiService = RetrofitClient.getInstance().getApiService(); } @Override public void login(String username, String password) { checkViewAttached(); getView().showLoading(); mApiService.login(username, password) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<BaseResponse<User>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(BaseResponse<User> userBaseResponse) { getView().hideLoading(); if (userBaseResponse.getCode() == 0) { getView().loginSuccess(userBaseResponse.getData()); } else { getView().showError(userBaseResponse.getMsg()); } } @Override public void onError(Throwable e) { getView().hideLoading(); getView().showError(e.getMessage()); } @Override public void onComplete() { } }); } } ``` 以上就是一个简单的 MVP 框架实现,您可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值