android系统架构MVP案例分析
2016-09-30 15:49
274 查看
前言
首先我们应该避免上帝类,上帝类(比如无所不能的Activity)的维护成本很高,你很难理解正在进行的操作,并且难以测试和扩展,这就是为什么要避免创建上帝类的黄金法则。在Android开发中,如果不考虑架构的话,Activity类往往会越来越大,最大的问题就是在Activity中同时存在业务逻辑和UI逻辑,这会增加测试和维护的成本。目前流行的开发架构有MVC、MVP、MVVM等,起先使用比较多的是MVC,但也会有很多问题,Android中经常会出现数千行的Activity代码,究其原因,Android中纯粹作为View的各个XML视图功能太弱,Activity基本上都是View和Controller的合体,既要负责视图的显示又要加入控制逻辑,承担的功能过多,代码量大也就不足为奇。所有更贴切的目前常规的开发说应该是View-Model 模式,大部分都是通过Activity的协调,连接,和处理逻辑的。
现重点说明下MVP架构:
- View: 对应于Activity、Fragment和xml,负责View的绘制以及与用户交互;
- Model: 依然是实体模型;它区别于mvc架构中的Model,在这里不仅仅只是数据模型。在MVP架构中Model它负责对数据的存取操作,例如对数据库的读写,网络的数据的请求等。
- Presenter: 负责完成View与Model间的交互和业务逻辑。在MVP中model和view无法直接进行交换。
MVP目前使用的较多,利用MVP的设计模型可以把部分的逻辑的代码从Fragment和Activity业务的逻辑移出来,在Presenter中持有View(Activity或者Fragment)的引用,然后在Presenter调用View暴露的接口对视图进行操作,这样有利于把视图操作和业务逻辑分开来。MVP能够让Activity成为真正的View而不是View和Control的合体,Activity只做UI相关的事。
至于再后来衍生的MVVM,是目前比较推荐使用的方法:
View: 对应于Activity和xml,负责View的绘制以及与用户交互;
Model: 实体模型;
ViewModel: 负责完成View于Model间的交互,负责业务逻辑。
MVVM的目标和思想与MVP类似,利用数据绑定(Data Binding)、依赖属性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一个更加灵活高效的架构。具体请看这里:如何构建Android MVVM应用程序。
事实上这几种框架没有谁比谁好,要视具体项目而选择。
Android官方MVP结构解读请移步:链接。
认清Android框架MVC,MVP和MVVM
项目结构
我这里以实现一个天气为案例。先看下结构:View:负责处理用户事件和视图部分的展示,它可能是Activity或者Fragment类。这里为:WeatherActivity。
Model:负责访问数据,数据可以是远端的ServerAPI,本地数据库或者SharedPreference等。这里为WeatherInfo。
Presenter:是链接(或者适配)View和Model的桥梁。这里为WeatherPresenter。
首先看View层,xml比较简单,就不显示了,WeatherActivity的源码如下:
import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import com.chinaso.testandroid.R; import com.victor.loading.rotate.RotateLoading; public class WeatherActivity extends AppCompatActivity implements WeatherContract.View{ private TextView weatherInfoTV; private RotateLoading rotateloading; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.weather_layout); initView(); WeatherPresenter presenter = new WeatherPresenter(this, new WeatherDataSourece()); presenter.loadWeather(); } private void initView() { weatherInfoTV = (TextView)findViewById(R.id.weatherInfoTV); rotateloading = (RotateLoading)findViewById(R.id.rotateloading); } @Override public void setPresenter(WeatherContract.Presenter presenter) { } @Override public void showLoadingIndicator(boolean isLoading) { } @Override public void showWeatherDetail(WeatherInfo info) { weatherInfoTV.setText(info.toString()); } @Override public void showLoading() { rotateloading.setVisibility(View.VISIBLE); rotateloading.start(); weatherInfoTV.setVisibility(View.GONE); } @Override public void hideLoading() { rotateloading.setVisibility(View.GONE); rotateloading.stop(); weatherInfoTV.setVisibility(View.VISIBLE); } @Override public void showNoContent() { weatherInfoTV.setText("暂无数据"); rotateloading.setVisibility(View.GONE); } }
Model层为实体类WeatherInfo。这个比较简单,就不显示了。我这里使用的字段有:
private String cityStr; private String weatherStr; private String tempStr; private String minIconStr; //set,get
Presenter层为WeatherPresenter,源码如下:
public class WeatherPresenter implements WeatherContract.Presenter { private WeatherContract.View mView; private WeatherDataSourece mData; public WeatherPresenter(WeatherContract.View view, WeatherDataSourece data) { this.mView = view; this.mData = data; mView.setPresenter(this); } @Override public void loadWeather() { mView.showLoading(); mData.fetchWeather(new WeatherDataSourece.LoadWeatherCallback() { @Override public void onLoaded(WeatherInfo info) { mView.hideLoading(); mView.showLoadingIndicator(false); if (info == null){ mView.showNoContent(); } else { mView.showWeatherDetail(info); } } @Override public void onDataNotAvailable() { mView.showNoContent(); } }); } }
Presenter的接口如下:
public interface WeatherContract { interface View { void setPresenter(Presenter presenter); void showLoadingIndicator(boolean isLoading); void showWeatherDetail(WeatherInfo info); void showLoading(); void hideLoading(); void showNoContent(); } interface Presenter { void loadWeather(); } }
其它使用到的有:
网络请求数据类WeatherDataSource:
() { @Override public void onResponse(String response) { if (!TextUtils.isEmpty(response)) { Gson gson = new Gson(); WeatherInfo bean = gson.fromJson(response, WeatherInfo.class); try { callback.onLoaded(bean); }catch (Exception e){} } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { callback.onDataNotAvailable(); } }); VolleyRequestManager.getRequestQueue().add(request); } } }" data-snippet-id="ext.9c00cb644ca271606aed657fbb8b175f" data-snippet-saved="false" data-codota-status="done">[code]import android.text.TextUtils; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.StringRequest; import com.chinaso.testandroid.VolleyRequestManager; import com.google.gson.Gson; public class WeatherDataSourece { private final String weatherURL = "xxx"; public interface LoadWeatherCallback { void onLoaded(WeatherInfo info); void onDataNotAvailable(); } public void fetchWeather(final LoadWeatherCallback callback) { if (weatherURL == null) { callback.onDataNotAvailable(); } else { StringRequest request = new StringRequest(weatherURL, new com.android.volley.Response.Listener<String>() { @Override public void onResponse(String response) { if (!TextUtils.isEmpty(response)) { Gson gson = new Gson(); WeatherInfo bean = gson.fromJson(response, WeatherInfo.class); try { callback.onLoaded(bean); }catch (Exception e){} } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { callback.onDataNotAvailable(); } }); VolleyRequestManager.getRequestQueue().add(request); } } }
网络请求VolleyRequestManager:
初始化最好在Application.onCreate()下:VolleyRequestManager.init(this);
import android.content.Context; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; public class VolleyRequestManager { private static RequestQueue mRequestQueue; private VolleyRequestManager() { } public static void init(Context context) { mRequestQueue = Volley.newRequestQueue(context); } public static RequestQueue getRequestQueue() { if (mRequestQueue != null) { return mRequestQueue; } else { throw new IllegalStateException("Not initialized"); } } }
这样就可以解耦了。
相关文章推荐
- Android图形系统的分析与移植 -- 二、Android显示系统软件架构分析
- Android 系统架构分析
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- Android MVP架构分析
- Android MVP架构分析
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- MVP架构-Android官方MVP项目和响应式MVP-RxJava项目架构分析对比解读
- Android Camera 系统架构源码分析(5)---->Camera数据Buf的传递方式及相关类
- 求 架构设计 的视屏和 设计模式的视频 性能优化 的视频 系统源码分析 的视频 android
- Android架构分析之基于Android系统的C应用程序开发
- Android系统架构分析 和 Android应用程序组件介绍
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- 基于Android Architecture Blueprints的MVP案例分析与实现
- Android图形系统的分析与移植--二、Android显示系统软件架构分析
- Android架构分析之基于Android系统的C应用程序开发
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- Android系统架构分析
- Android架构实例分析之编写hello驱动的系统硬件服务
- rk3188--5.android input 系统架构分析