描述
MVP模式是什么?MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
MVC和MVP的区别?
为什么会出现MVP模式呢?这是因为原有的MVC模式有一些短板。比如在android开发中,activity充当着MVC中Controller的角色,但是在实际开发中处理view的逻辑和角色。当业务界面复杂时我的activity会显得很庞大。于是出现了MVP模式,它新增了一个Presenter角色用于处理数据和界面的模型以及逻辑,Activity仅仅用于展示界面和用户交互,这样就解决了MVC中角色不清的局面。
所以,MVP与MVC的重大区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,即View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
MVC模式结构
- Model 业务逻辑和实体模型
- Controller 对应Activity
- View 视图以及布局文件
MVP模式结构
- Model: 业务逻辑和实体模型
- View:用户交互和视图显示,在android中对应activity
- Presenter: 负责完成View于Model间的逻辑和交互
小节:MVP模式相当于在MVC模式中又加了一个Presenter用于处理模型和逻辑,将View和Model完全独立开,在android开发中的体现就是activity仅用于显示界面和交互,activity不参与模型结构和逻辑,
#### 实战
谷歌官网给了我们一个MVP模式实战的例子,它是一个类似记事本的app,源码地址在:https://github.com/googlesamples/android-architecture
官方案例的框架图如下:
看完源码后发现其不适合初学者理解,于是我自己写了一个demo方便大家理解。
demo源码地址:https://github.com/halibobo/AndroidMvpExample
源码主要类的结构如下
下面来看源码
View层对应的是MainActivity,它继承了抽离出View所有操作方法的接口OperationView
/** * *Created by su on 2016/6/22. */ public interface OperationView { void showCreatingPhone(); void showPhoneCountChange(); void showNoPhone(); void showFactoryBusy(); void showCreatedPhone(); }
MainActivity具体对每个操作进行了具体的实现
1 public class MainActivity extends AppCompatActivity implements OperationView { 2 3 private ListView listView; 4 5 private Button btnCreate; 6 7 private PhonePresenter phonePresenter; 8 9 private ProgressDialog mLoadingDialog; 10 ArrayAdapter<Phone> arrayAdapter; 11 private Toast toast; 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 17 setSupportActionBar(toolbar); 18 19 PhoneFactory phoneFactory = new PhoneFactory(); 20 phoneFactory.createPhone("nokia",555); 21 phonePresenter = new PhonePresenter(phoneFactory, this); 22 btnCreate = (Button) findViewById(R.id.btnCreate); 23 listView = (ListView) findViewById(R.id.listView); 24 arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, phoneFactory.getPhonesList()); 25 btnCreate.setOnClickListener(new View.OnClickListener() { 26 @Override 27 public void onClick(View v) { 28 phonePresenter.addPhone(new Phone("iphone", 4000+ new Random().nextInt(1000))); 29 } 30 }); 31 32 listView.setAdapter(arrayAdapter); 33 34 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 35 @Override 36 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 37 phonePresenter.removePhone(position); 38 } 39 }); 40 41 } 42 43 @Override 44 public void showCreatingPhone() { 45 mLoadingDialog = new ProgressDialog(this); 46 mLoadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 47 mLoadingDialog.setMessage("工厂正在生产手机"); 48 mLoadingDialog.setCancelable(true); 49 mLoadingDialog.show(); 50 } 51 52 @Override 53 public void showPhoneCountChange() { 54 arrayAdapter.notifyDataSetChanged(); 55 } 56 57 @Override 58 public void showNoPhone() { 59 findViewById(R.id.noPhone).setVisibility(View.VISIBLE); 60 } 61 62 @Override 63 public void showFactoryBusy() { 64 showToast("工厂繁忙,请稍后再试!"); 65 } 66 67 @Override 68 public void showCreatedPhone() { 69 if (mLoadingDialog != null && mLoadingDialog.isShowing()) { 70 mLoadingDialog.dismiss(); 71 } 72 mLoadingDialog = null; 73 showToast("新生产出一台手机!"); 74 findViewById(R.id.noPhone).setVisibility(View.GONE); 75 } 76 77 private void showToast(String string) { 78 if (toast == null) { 79 toast = Toast.makeText(this, string, Toast.LENGTH_SHORT); 80 }else{ 81 toast.setText(string); 82 } 83 toast.show(); 84 } 85 }
Model层对应的是PhoneFactory,它处理和数据相关的一些简单操作
1 /** 2 * Created by su on 2016/6/22. 3 */ 4 5 /** 6 * 手机工厂类 7 */ 8 9 public class PhoneFactory { 10 private ArrayList<Phone> phonesList = new ArrayList<>(); 11 12 13 public void addPhone(Phone phone) { 14 phonesList.add(phone); 15 } 16 17 public void removePhone(Phone phone) { 18 phonesList.remove(phone); 19 } 20 21 public void removePhone(int index) { 22 if (index >= 0 && index < phonesList.size()) { 23 phonesList.remove(index); 24 } 25 } 26 27 public void createPhone(String name, double price) { 28 Phone phone = new Phone(name, price); 29 phonesList.add(phone); 30 } 31 32 public ArrayList<Phone> getPhonesList() { 33 return phonesList; 34 } 35 36 public int getPhoneCounts() { 37 return phonesList.size(); 38 } 39 }
下面是最为重要的Presenter层 对应代码中的PhonePresenter,它处理界面逻辑和数据模型等,源码如下:
1 public class PhonePresenter implements TaskPresenter{ 2 3 private final PhoneFactory phoneFactory; 4 private final OperationView operationView; 5 6 private static final long createPhoneTime = 2000; 7 private static final int msgWhat = 0x102; 8 9 10 public PhonePresenter(@NonNull PhoneFactory phoneFactory, @NonNull OperationView operationView) { 11 this.phoneFactory = phoneFactory; 12 this.operationView = operationView; 13 } 14 15 @Override 16 public void addPhone(Phone phone) { 17 operationView.showPhoneCountChange(); 18 if (mHandler.hasMessages(msgWhat)) { 19 operationView.showFactoryBusy(); 20 return; 21 } 22 Message message = new Message(); 23 message.what = msgWhat; 24 message.obj = phone; 25 mHandler.sendMessageDelayed(message, createPhoneTime); 26 operationView.showCreatingPhone(); 27 } 28 29 @Override 30 public void removePhone(int index) { 31 phoneFactory.removePhone(index); 32 if (phoneFactory.getPhoneCounts() <= 0) { 33 operationView.showNoPhone(); 34 } 35 operationView.showPhoneCountChange(); 36 } 37 38 @Override 39 public void removePhone(Phone phone) { 40 41 } 42 43 public ArrayList<Phone> getPhones() { 44 ArrayList<Phone> phones = phoneFactory.getPhonesList(); 45 if (phones.isEmpty()) { 46 operationView.showNoPhone(); 47 } 48 return phones; 49 } 50 51 private Handler mHandler = new Handler() { 52 @Override 53 public void handleMessage(Message msg) { 54 super.handleMessage(msg); 55 phoneFactory.addPhone((Phone)msg.obj); 56 operationView.showCreatedPhone(); 57 operationView.showPhoneCountChange(); 58 } 59 }; 60 }
运行效果图如下:
总结
使用MVP模式会使得代码多出一些接口但是使得代码逻辑更加清晰,尤其是在处理复杂界面和逻辑时,我们可以对同一个activity将每一个业务都抽离成一个Presenter,这样代码既清晰逻辑明确又方便我们扩展。当然如果我们的业务逻辑本身就比较简单的话使用MVP模式就显得,没那么必要。所以我们不需要为了用它而用它,具体的还是要要业务需要