編輯:關於Android編程
MVC、MVP、MVVP相信大家已經耳熟能詳了,作為Android最出名的三個框架,它們的應用是非常的廣泛。這篇博客就來簡單介紹下其中二種框架。也加強下自己對這方面的了解。由於自己菜鳥一枚,有不對和需要補充的地方歡迎評論~
MVC全名是:Model(模型) View(視圖) Controller(控制器) 是軟件架構中最常見的框架,簡單來說,就是通過Controller的控制去操作Model層的數據,並且返回給View作展示,具體見下圖:
當用戶觸發了一個事件,View層會將這個事件發送給Controller控制層,然後Controller會通知Model層更新數據,Model層更新完數據之後會直接顯示在View層上,這就是MVC的工作原理了。
說到這裡可能就有人會說了,明明一個Activity就能搞定的事,為什麼還分三個類來寫,煩不煩?
So,那為什麼要采用架構設計呢?
通過設計,可以使我們的程序模塊化,做到模塊內部的高聚合和模塊之間的低耦合。這樣做的好處是,當我們在開發中只需專注一點即可!提高程序的開發效率,並且更容器做後期的測試以及定位問題。但是,我們不能為了設計而設計,為了架構而架構。對於不同量級的工程,具體的架構實現方法必然是不同的。
在Android中,MVC的應用無處不在,如:我們經常寫的layout.xml就對應MVC的View層,裡面都是一些View的布局代碼,而各種JavaBean和Adapter就類似與MVC中的Model層,Activity就是Controller層了。下面用一個登錄的小例子來演示一下。
首先看下常規寫法,把所有邏輯都放在Activity裡面的:
* activity_login.xml布局:
LoginActivity代碼:
public class LoginActivity extends AppCompatActivity {
private EditText mUsername;
private EditText mPassword;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
}
private void initView() {
dialog = new ProgressDialog(this);
mUsername = (EditText) findViewById(R.id.et_login_username);
mPassword = (EditText) findViewById(R.id.et_login_password);
}
// 登錄
public void login(View v){
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
final User user = new User();
user.username = username;
user.password = password;
if(checkUser(user)){
dialog.show();
// 模擬請求登錄接口
new Thread(new Runnable() {
@Override
public void run() {
// 模擬耗時操作
SystemClock.sleep(2000);
if ("123".equals(user.username) && "123".equals(user.password)){
runOnUiThread(new Runnable() {
@Override
public void run() {
onLoginSuccess();
}
});
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
onLoginError();
}
});
}
}
}).start();
}else{
Toast.makeText(getApplicationContext(), "用戶名或密碼不能為空", Toast.LENGTH_SHORT).show();
}
}
/**
* 登錄成功
*/
private void onLoginSuccess() {
Toast.makeText(getApplicationContext(), "登錄成功", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
/**
* 登錄失敗
*/
private void onLoginError() {
Toast.makeText(getApplicationContext(), "登錄失敗", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
/**
* 判斷用戶輸入的信息是否正確
* @param user
* @return
*/
private boolean checkUser(User user) {
// 在這裡由於演示,所以只做簡單非空判斷,實際開發中會判斷更多情況
if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
return false;
}
return true;
}
}
UserBean:
public class User {
public String username;
public String password;
}
這是一個超級簡化版的登錄,但是差不多已經有上百行代碼了,如果都寫出來的話,那麼就太多了,所以我們就需要遵循某種模式來進行拆分,下面是用MVC模式進行拆分後的代碼:
新增一個類,將請求網絡的操作抽取出來(這裡由於模擬請求網絡所以代碼並不多):
public class LoginModel {
/**
* 請求網絡,發送登錄消息
* @return
*/
public boolean sendLoginInfo(User user){
SystemClock.sleep(2000);
if ("123".equals(user.username) && "123".equals(user.password)){
return true;
}else{
return false;
}
}
}
修改Activity中的請求網絡為通過創建Model調用方法去判斷登錄是否成功:
LoginModel loginModel = new LoginModel();
if(loginModel.sendLoginInfo(user)) {
runOnUiThread(new Runnable() {
@Override
public void run() {
onLoginSuccess();
}
});
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
onLoginError();
}
});
}
到這裡一個簡單版的MVC抽取就完成了,其實代碼並沒有少很多,這是因為Activity存在了兩部分內容,一部分是業務相關的,一部分是界面相關的。在這裡問題就產生了,如果界面復雜或業務多的話,會導致Activity裡面的東西會很龐大,V裡面的信息就只有一個Layout,而C也就是Activity裡的東西則異常龐大。
那麼這個時候,我們可以選擇將業務部分的代碼進行拆分,其對應的模式就是:MVP。
如果將Activity中界面相關的內容進行拆分,其對應的模式就是MVVM。
下面簡單講解一下MVP模式,MVVM由於涉及到data binding框架,東西比較多,下次再寫。
MVP
MVP是從經典的MVC中演變過來的,在思想上,它們有想通的地方:
Controller/Presenter負責邏輯的處理,Model用於提供數據,View負責顯示。
原來的Model層不變,Activity合並到View層,Presenter用於鏈接View層和Model層。
下面用MVP模式重構上面的登錄小例子:
布局文件和Bean如上,就不貼了。
分包:
注意:Activity並不是View,setContextView()中的xml文件才是。
V (Activity) 代碼:
public class LoginActivity extends AppCompatActivity {
private EditText mUsername;
private EditText mPassword;
private ProgressDialog mDialog;
private LoginPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUsername = (EditText) findViewById(R.id.et_login_username);
mPassword = (EditText) findViewById(R.id.et_login_password);
mDialog = new ProgressDialog(this);
mPresenter = new LoginPresenter(this);
}
public void login(View v){
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
final User user = new User();
user.username = username;
user.password = password;
if(mPresenter.checkUserInfo(user)){
mDialog.show();
mPresenter.login(user);
}else{
Toast.makeText(getApplicationContext(), "用戶名或密碼不能為空", Toast.LENGTH_SHORT).show();
}
}
/**
* 登錄成功
*/
public void loginSuccess() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "登錄成功", Toast.LENGTH_SHORT).show();
mDialog.dismiss();
}
});
}
/**
* 登錄失敗
*/
public void loginError(){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "登錄失敗", Toast.LENGTH_SHORT).show();
mDialog.dismiss();
}
});
}
}
P (LoginPresenter)代碼:
public class LoginPresenter {
private LoginActivity mView;
public LoginPresenter(LoginActivity view){
this.mView = view;
}
/**
* 校驗用戶信息
* @param user 用戶信息
* @return true:校驗成功 false:校驗失敗
*/
public boolean checkUserInfo(User user){
// 在這裡由於演示,所以只做簡單非空判斷,實際開發中會判斷更多情況
if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
return false;
}
return true;
}
/**
* 登錄
* @param user 用戶信息
*/
public void login(final User user){
new Thread(new Runnable() {
@Override
public void run() {
LoginModel loginModel = new LoginModel();
if(loginModel.sendLoginInfo(user)){
mView.loginSuccess();
}else{
mView.loginError();
}
}
}).start();
}
}
M(LoginModel)代碼:
public class LoginModel {
/**
* 請求網絡,發送登錄消息
*/
public boolean sendLoginInfo(User user) {
// 模擬請求網絡操作
SystemClock.sleep(2000);
if ("123".equals(user.username) && "123".equals(user.password)) {
return true;
} else {
return false;
}
}
}
至此,一個簡單的MVP模式的登錄小例子就出來了,相比於MVC,MVP中的代碼已經相當簡潔了,各模塊之間分工明確。
View層負責和UI進行交互,Model層負責提供數據,Presenter層負責處理業務邏輯並處理Model層和View層的通信。
相信大家也看出來了,MVP主要解決就是把邏輯層給抽出來成P層,還有就是不和MVC中的Molder層可以操作View層一樣,而是由Presenter負責通信的角色。這樣做的好處是,如果有邏輯上的修改,那麼我們直接修改Presenter層的東西就可以了。
到這裡,並沒有完善,我們還可以擴展下,上面的LoginActivity的構參中,我們傳入了一個LoginActivity:
public LoginPresenter(LoginActivity view){
this.mView = view;
}
這裡其實並不合理,因為LoginActivity是屬於V層的東西,而我們在開發過程中,V層不光會有Activity,也會有Fragment,如果按照上面寫,通用性並不好,這個時候,接口開發就派上用場了。
修改構參為接口:
public LoginPresenter(IUserLoginView view){
this.mView = view;
}
創建IUserLoginView接口:
package com.airsaid.mvpdemo.i;
/**
* Created by zhouyou on 2016/5/4.
*/
public interface IUserLoginView {
/**
* 登錄成功
*/
void loginSuccess();
/**
* 登錄失敗
*/
void loginError();
}
讓LoginActivity去實現這個接口:
public class LoginActivity extends AppCompatActivity implements IUserLoginView
到這裡就擴展完成了,這只是一個MVP的簡單擴展,具體大家可以看看Android官方的MVP架構實例項目。
在這裡推薦一篇相關文章,大家感興趣可以看看:Android官方MVP架構實例項目解析
在平時開發過程中,MD5加密是一個比較常用的算法,最常見的使用場景就是在帳號注冊時,用戶輸入的密碼經md5加密後,傳輸至服務器保存起來。雖然md5加密經常用,但是md5的
在res下新建一個文件夾,命名為anim,創建xml文件,例如創建了一個a1.xml //在這裡寫動畫 alpha:漸變透明度效果 rotate:旋轉動
背景一般情況下,為了讓用戶更方便的打開應用,程序會在桌面上生成一些快捷方式。本來呢,如果是原生的桌面,其實是十分簡單,直接調用系統相關的API就行了。但是眾多的系統廠商以
簡介項目需要...直接展示效果吧:原理使用UGUI提供的ScrollRect和ScrollBar組件實現基本滑動以及自己控制每次移動一頁來達到滑頁的效果。實現過程1.創建