Android中使用MVP组件化,Android组件化-经典MVP

一、经典的MVP

1.1 一句话MVP

提到Android MVP(Model-View-Presenter)就会想到MVC(Model-View-Controller),C就是Web开发中经常提到的Controller,P则是Android中用来分离Activity逻辑与界面的Presenter。

MVP核心思想:

MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。

1.2 MVP图解

一图胜千言:

240ac2cdc2798c5d3c65ab7462c4e7b1.png

视图View:Activity和Fragment

逻辑Presenter:业务逻辑和业务管理类等

模型Model:SharedPreferences、数据库访问(Dao)和网络交互(Api)

二、Modulize使用MVP

Modulize项目使用MVP作为基本的开发框架(以登录为例)。

2.1 Model层的设计

Model层负责数据交互,包括网络交互、本地数据库交互以及SharedPreferences数据存取。在lib-common中添加抽象类BaseModel,LoginModel等业务模块继承自BaseModel。

public abstract class BaseModel {

}

网络交互 - okHttp+Retrofit+Rxjava

网络访问使用无话可说的okHttp,结合优雅的Retrofit,加以RxJava,真香!

使用okHttpClient实例管理全局http访问:

public class OkHttp3Util {

private static OkHttpClient mOkHttpClient;

/** * 获取OkHttpClient对象实例 * * @return */ public static OkHttpClient getOkHttpClient() {

if (null == mOkHttpClient) {

// build design mode mOkHttpClient = new OkHttpClient.Builder() // cookie manager .cookieJar(new CookiesManager()) // 网络请求日志 .addInterceptor(loggingInterceptor) // 自定义拦截器 .addInterceptor(new CommonIntercepter()) // set timeout of connection, reading and writing .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .cache(cache) .build(); }

return mOkHttpClient; }}

在lib-common中新建ServiceGenerate类管理、创建Retrofit接口访问实例,

public class ServiceGenerator {

private static final String API_SERVICE = "http://xxxx:8080/api/";

/** * 在gson中加入时间格式化,DateDeserializer\DateSerializer为自定义转换类. */ private static Gson gson = new GsonBuilder() .registerTypeAdapter(java.util.Date.class, new DateDeserializer()).setDateFormat(DateFormat.LONG) .registerTypeAdapter(java.util.Date.class, new DateSerializer()).setDateFormat(DateFormat.LONG) .create();

/** * API Retrofit. */ private static Retrofit apiGenerator = new Retrofit.Builder() .baseUrl(API_SERVICE) // 自定义转换器一定要在gsonConverter前面,否则gson会拦截所有的解析方式 .addConverterFactory(CustomConverterFactory.create()) // Gson Converter .addConverterFactory(GsonConverterFactory.create(gson)) // Callback Handler RxJava .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(OkHttp3Util.getOkHttpClient()) .build();}

为了统一处理Http接口返回,创建Response响应类,应当和后台接口保持一致的gson格式:

public class Response {

private int code; private String message; private T data;

...}

基于Retrofit的登录Api如下:

public interface LoginApi {

/** * user login * * @param username username * @param password password * @return user info */ @FormUrlEncoded @POST("login") Observable> loginStu(@Field("username") String username, @Field("password") String password);}

数据库交互 - GreenDao

使用J神家的的GreenDao,这个移动端ORM框架还是需要好好学习下的,本文仅介绍GrrenDao在MVP中的使用。在lib-db中创建DBHelper用于管理数据库连接和数据访问对象(Dao)实例:

public class DBHelper {

... instance init

public AbstractDao getDao(Class clazz) { return session.getDao(clazz); }}

SharedPreferences

使用SP存储用户偏好设置或登录认证数据等碎片数据。

LoginModel

Model中持有Retrofit实例(api)、数据库访问对象(Dao)以及SP等本地存储对象:

public class LoginModel extends BaseModel {

private static final String TAG = "LoginModel";

private LoginApi api; private UserDao userDao; private SharedPreferences userPreference;

public LoginModel() { // 使用ServiceGenerator生成api访问类 api = ServiceGenerator.createAPIService(LoginApi.class); // 获取数据库访问对象 userDao = (UserDao) DBHelper.getInstance().getDao(User.class); userPreference = context.getSharedPreferences("user", Context.MODE_PRIVATE); }

public void setUser(User user) { userPreference.put("user", user.getName()); userDao.insert(user); }

public void login(String username, String password, Observer> observer) { rxSubscribe(api.login(username, password), observer); }}

Presenter调用LoginModel方法时传递接口参数和Observer,LoginModel接口请求响应后回调Observer,rxSubscribe()定义在BaseModel中:

public abstract class BaseModel { protected static void rxSubscribe(Observable observable, Observer observer) { observable.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.newThread())//子线程访问网络 .observeOn(AndroidSchedulers.mainThread())//回调到主线程 .subscribe(observer); }}

2.2 Presenter层的设计

Presenter持有Model实例,Presenter初始化时实例化Model,在lib-common中加入BasePresenter:

public abstract class BasePresenter {

protected TView mView; protected TModel mModel;

public BasePresenter(TView view) { this.mView = view; this.mModel = this.getModel(); }

protected abstract TModel getModel();

public void detach() { this.mView = null; }}

LoginPresenter集成BasePresenter,实例化LoginModel:

public class LoginPresenter extends BasePresenter {

public LoginPresenter(BaseActivity activity) { super(activity); }

@Override protected LoginModel getModel() { return new LoginModel(); }

public void login(String username, String password) { // 请求前 加载等待框 mView.loadHud(); mModel.loginStudent(username, password, new Observer>() { @Override public void onCompleted() { }

@Override public void onError(Throwable e) { e.printStackTrace(); }

@Override public void onNext(Response response) { // 加载完成 取消等待框 mView.cancelHud();

if (response.OK()) { // 请求成功 回调VIew层进行页面刷新 mView.onViewEvent(BaseView.VIEW_LOADED, response.getData()); // 把用户信息保存在本地 mModel.setUser(user); } else { // 请求失败 回调View层报错 mView.onViewEvent(LoginActivity.ERROR, null); } } }); }}

本项目在MVP中未使用接口的方式,在View中实现接口,在Presenter中持有实例并进行接口调用,因为使用接口则每个页面都需要新建一个接口类,较为繁琐。

本项目MVP使用BaseView中的抽象方法onViewEvent(),每个View继承BaseView后实现onViewEvent(int code, Object param),Presenter层Attach BaseView后通过mView.onViewEvent()对View进行界面回调处理,View中根据事件code和参数param进行视图处理。

一个Presenter可持有多个Model,定义多个Model对象并在Presenter构造函数中初始化。

2.3 View层的设计

在lib-common中定义BaseView,

void toast(@StringRes int resId);

/** * 用于Presenter中吐司提示 */ void toast(String res);

T findViewById(@IdRes int resId);

/** * 用于Presenter回调界面操作 */ void onViewEvent(int code, Object param);

/** * 在界面中统一处理数据、网络异常 */ void onViewState(int state); void onViewState(Response response);

/** * 加载、取消Dialog */ void loadHud(); void cancelHud();

}

toast():Toast封装,用于在Activity、Fragment或Presenter中弹出用户提示

findViewById():主要用于fragment中获取元素使用(组件化开发使用ButterKnife较为繁琐,不建议使用)

onViewEvent():View层的回调,用于Presenter网络请求响应后通知View层

onViewState():View层的回调。当Presenter层发生错误时统一处理View(网络异常、Http请求错误等)

loadHud()/cancelHud():加载ProgressDialog,Presenter发请网络请求时、请求结束后,在Presenter层弹出ProgressDialog

BaseActivity

public abstract class BaseActivity extends AppCompatActivity implements BaseView {

protected Handler mUIHandler;

protected TPresenter mPresenter;

protected KProgressHUD mHud;

// 获取界面layout资源文件 @LayoutRes protected abstract int getLayoutResId();

protected abstract void initViewAndData(@Nullable Bundle savedInstanceState);

protected abstract TPresenter getPresenter();

@Override public void onCreate(@Nullable Bundle savedInstanceState) { beforeCreate(); super.onCreate(savedInstanceState); beforeSetContentView(); setContentView(this.getLayoutResId()); // init this.init(); this.initViewAndData(savedInstanceState);

// EventBus EventBus.getDefault().register(this); }

/** * before set contentView */ private void beforeSetContentView() { // NoTitle requestWindowFeature(Window.FEATURE_NO_TITLE); // ScreenPortrait setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); }

/** * before create */ private void beforeCreate() { // 统一设置主题 setTheme(UIConfig.getInstance(getApplicationContext()).getThemeId()); }

@Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(CommonEvent event) { // EventBus统一处理全局异常 BLog.e("[Event]: " + event.code); if (event.code == CommonEvent.Type.NETWORK_ERROR) { onViewState(UIConstants.ViewState.NETWORK_DISCONNECTED); if (this.mCommonEvent != null) { this.mCommonEvent.onCommonEvent(event.code, event.param); } // cancel loading hud cancelHud(); } }

/** * init view, e.g commonTitleBar. */ private void init() { // view init ...

// 从子类拿到Presenter实例 this.mPresenter = this.getPresenter(); // 使用第三方库作为Loading Dialog this.mHud = KProgressHUD.create(this); }

@Override public void onViewState(int state) { // 全局异常处理 ... }

@Override public void onViewState(Response response) { // 根据Response处理服务器http响应 ... }

@Override protected void onDestroy() { super.onDestroy();

this.mUIHandler = null; // Unregister EventBus EventBus.getDefault().unregister(this); }

@Override public T findViewById(int resId) { return super.findViewById(resId); }

@Override public void toast(@StringRes final int resId) { if (mUIHandler == null) { return; } mUIHandler.post(new Runnable() { @Override public void run() { Toast.makeText( getApplicationContext(), resId, Toast.LENGTH_SHORT).show(); } }); }

@Override public void toast(final String res) { ... }

@Override public void loadHud(int resId) { // 加载等待Dialog if (mHud == null) { mHud = KProgressHUD.create(this); } mHud.setStyle(KProgressHUD.Style.SPIN_INDETERMINATE) .setCancellable(true) .setLabel(resId == 0 ? getString(R.string.opt_loading) : getString(resId)) .setAnimationSpeed(1) .setDimAmount(0.5f) .show(); }

@Override public void cancelHud() { if (mHud != null) { mHud.dismiss(); } }}

loadHud():参考第三方库https://github.com/Kaopiz/KProgressHUD

BaseFragment

类似BaseActivity,加入一些对宿主Activity的回调。

参考https://github.com/blackist/modulize/blob/8478eb2a4bdaf7b9f9e2022be0e9462ea82b3eeb/lib-common/src/main/java/org/blackist/common/base/BaseFragment.java

LoginActivity

LoginActivity继承自BaseActivity,实例化LoginPresenter,实现onViewEvent()回调函数:

public class LoginActivity extends BaseActivity implements View.OnClickListener {

private static final String TAG = "LoginActivity";

public static final int ERROR = 1000;

@Override protected int getLayoutResId() { return R.layout.main_login_activity; }

@Override protected void initViewAndData(@Nullable Bundle savedInstanceState) { initView(); ... }

@Override protected LoginPresenter getPresenter() { return new LoginPresenter(this); }

@Override public void onClick(View v) { ... }

@Override public void onViewEvent(int code, Object param) { switch (code) { case VIEW_LOADED: { // 登录成功处理 ... startActivity(new Intent(this, MainActivity.class)); finish(); } break;

case ERROR: { toast(R.string.main_login_error); } break;

default: } }}

通常情况下一个View对应一个Presenter,也可在View中定义多个Presenter对象并在initViewAndData()中初始化

至此,实现了精简版的Android MVP,本人用在项目开发中问题不大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值