1. 程式人生 > >Android_MVP開發模式登入註冊案例

Android_MVP開發模式登入註冊案例

首先MVP 是從經典的MVC架構演變而來

 系統C/S(Client/Server)三層架構模型:

  1)檢視層(View):一般採用XML檔案對應用的介面進行描述,使用的時候可以直接引入,極為方便,可以的大大縮短開發時間,也可以使用JavaScript+HTML等的方式作為View層,當然這裡需要進行Java和JavaScript之間的通訊,幸運的是,Android提供了非常方便的通訊實現。業務邏輯層(BLL):它的關注點主要集中在業務規則的制定、業務流程的實現等與業務需求有關的系統設計,也即是說它是與系統所應對的領域(Domain)邏輯有關,很多時候,也將業務邏輯層稱為領域層。

  2)控制層(Controller):Android的控制層的重任通常落在了眾多的Acitvity的肩上,這句話也就暗含了不要在Acitivity中寫程式碼,要通過Activity交割Model業務邏輯層處理。

  3)模型層(Model):對資料庫的操作、以及其他和資料有關的的操作都應該在Model裡面處理,當然對業務計算等操作也是必須放在的該層的。就是應用程式中二進位制的資料。

三層結構架構三層間的互動及作用如下圖所示:

MVP中View並不直接使用Model,它們之間的通訊是通過Presenter (MVC中的Controller)來進行的,所有的互動都發生在Presenter內部,而在MVC中View會從直接Model中讀取資料而不是通過 Controller。
在MVP裡,Presenter完全把Model和View進行了分離,主要的程式邏輯在Presenter裡實現。而且,Presenter與具體的View是沒有直接關聯的,而是通過介面進行互動,從而使得在變更View時候可以保持Presenter的不變,可以多次複用。
在MVP裡,應用程式的邏輯主要在Presenter來實現,其中的View是很薄的一層,只應該有簡單的Set/Get的方法,使用者輸入和設定介面顯示的內容,除此就不應該有更多的內容,絕不容許直接訪問Model。 MVP主要解決就是把邏輯層抽出來成P層,要是遇到需求邏輯上的更改就可以只需要修改P層了或者遇到邏輯上的大改我們可以直接重寫一個P也可以,很多開發人員把所有的東西都寫在了Activity/Fragment裡面這樣一來遇到頻繁改需求或者邏輯越來越複雜的時候,Activity /Fragment裡面就會出現過多的混雜邏輯導致出錯,所以MVP模式對於APP來對控制邏輯和UI的解耦來說是一個不錯的選擇。

開始例項

一:佈局

1:登入頁面佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="activity.example.com.mvpframework.view.MainActivity">

    <EditText
        android:id="@+id/ed_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入手機號"
        android:layout_above="@+id/ed_pass"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginBottom="55dp" />

    <EditText
        android:id="@+id/ed_pass"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入密碼"
        android:layout_above="@+id/btn_login"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginBottom="94dp" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="登入"
        android:layout_marginLeft="65dp"
        android:layout_marginStart="65dp"
        android:layout_alignBaseline="@+id/btn_regis"
        android:layout_alignBottom="@+id/btn_regis"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Button
        android:id="@+id/btn_regis"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="註冊"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_marginRight="43dp"
        android:layout_marginEnd="43dp"
        android:layout_marginBottom="130dp" />

</RelativeLayout>
2:註冊頁面佈局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="activity.example.com.mvpframework.view.RegisActivity">

    <EditText
        android:id="@+id/ed_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入手機號"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="61dp" />

    <EditText
        android:id="@+id/ed_pass"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入密碼"
        android:layout_marginBottom="63dp"
        android:layout_above="@+id/btn_regis"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Button
        android:id="@+id/btn_regis"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="註冊"
        android:layout_marginBottom="169dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginLeft="130dp"
        android:layout_marginStart="130dp" />

</RelativeLayout>

3:登入成功跳轉的頁面,在此就不寫了

二:定義了兩個介面

1:登入、註冊 成功或失敗的介面

public interface Login {
    void succeed(String data);
    void failed(String message);
}

2:請求網路成功或失敗的介面

public interface Http {
    void onSuccess(String data);
    void onFiled(String message);
}

三:M(model)層,提供資料,單例模式
public class HttpUtils {
    private static volatile HttpUtils instance;

    private HttpUtils(){

    }

    public static HttpUtils getInstance(){
        if(instance==null){
            synchronized (HttpUtils.class){
                if(instance==null){
                    instance = new HttpUtils();
                }
            }
        }
        return instance;
    }

    public void get(String url, Map<String, String> map, final Http http) {
        RequestParams params = new RequestParams(url);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            params.addQueryStringParameter(entry.getKey(), entry.getValue());
        }
        x.http().get(params, new Callback.CommonCallback<String>() {
            @Override
            public void onSuccess(String result) {
                http.onSuccess(result);
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {
                http.onFiled(ex.getMessage());
            }

            @Override
            public void onCancelled(CancelledException cex) {

            }

            @Override
            public void onFinished() {

            }
        });
    }
}


四:P(Presenter)層,邏輯處理

public class LoginPresenter {

    private Login login;
    private Context context;

    // 提供初始化IView物件的一個方法

    public LoginPresenter() {

    }
    
    public LoginPresenter(Login login,Context context) {
        this.login = login;
        this.context=context;
    }

    public boolean checkData(String phone,String pass) {
        if(TextUtils.isEmpty(phone)||TextUtils.isEmpty(pass)){
            Toast.makeText(context,"使用者名稱或密碼不能為空",Toast.LENGTH_SHORT).show();
            return false;
        }
        //驗證是否為手機號的正則表示式
        String regex = "^1[3|4|5|7|8]\\d{9}";
        if(!Pattern.matches(regex,phone)){
            Toast.makeText(context,"手機號格式不正確",Toast.LENGTH_SHORT).show();
            return false;
        }
        if(pass.length()<6){
            Toast.makeText(context,"密碼長度需要大於6位",Toast.LENGTH_SHORT).show();
            return false;
        }
        return true;
    }

    public void login(String url,String username, String password) {
        Map<String, String> map = new HashMap<>();
        map.put("mobile", username);
        map.put("password", password);
        HttpUtils.getInstance().get(url, map,
                new Http() {
                    @Override
                    public void onSuccess(String data) {
                        //解析
                        Gson gson = new Gson();
                        DataBean dataBean = gson.fromJson(data, DataBean.class);
                        //獲取code值,返回0為成功,1為失敗
                        if(dataBean.getCode().trim().equals("0")){
                            login.succeed(data);
                            Toast.makeText(context,dataBean.getMsg(),Toast.LENGTH_SHORT).show();
                        }else{
                            Toast.makeText(context,dataBean.getMsg(),Toast.LENGTH_SHORT).show();
                        }
                    }
                    @Override
                    public void onFiled(String message) {
                        login.failed(message);
                    }
                });
    }

    //防止記憶體洩漏
    public void detatch(){
        if (login != null) {
            login = null;
        }
    }
}

五:V(View)層,展示頁面,不做邏輯處理

1:MainActivity登入頁面

public class MainActivity extends AppCompatActivity implements Login,View.OnClickListener{

    private EditText ed_phone;
    private EditText ed_pass;
    private Button btn_login;
    private Button btn_regis;
    private LoginPresenter presenter1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        //獲取id
        ed_phone = (EditText)findViewById(R.id.ed_phone);
        ed_pass = (EditText)findViewById(R.id.ed_pass);
        btn_login = (Button)findViewById(R.id.btn_login);
        btn_regis = (Button)findViewById(R.id.btn_regis);
        //點選監聽
        btn_login.setOnClickListener(this);
        btn_regis.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_login:
                String user = ed_phone.getText().toString().trim();
                String pass = ed_pass.getText().toString().trim();
                //例項化p層
                presenter1 = new LoginPresenter(this,MainActivity.this);
                //效驗輸入的值是否符合格式
                boolean b = presenter1.checkData(user, pass);
                if(b){
                    presenter1.login("http://120.27.23.105/user/login",user,pass);
                }
                break;

            case R.id.btn_regis:
                //跳到註冊頁面
                startActivity(new Intent(MainActivity.this,RegisActivity.class));
                break;
        }
    }

    @Override
    public void succeed(String data) {
            //登入成功跳到另一個頁面
            startActivity(new Intent(MainActivity.this,ShopActivity.class));
    }

    @Override
    public void failed(String message) {
        //正常情況不會執行,只有請求網路失敗的時候才會執行,這裡的失敗是指網頁打不開的失敗
        Toast.makeText(MainActivity.this,"網路異常,登入失敗",Toast.LENGTH_SHORT).show();
    }

    //防止記憶體洩漏
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter1 != null) {
            presenter1.detatch();
        }
    }

}
2:註冊頁面
public class RegisActivity extends AppCompatActivity implements Login,View.OnClickListener{

    private EditText ed_phone;
    private EditText ed_pass;
    private Button btn_regis;
    private LoginPresenter presenter1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_regis);
        ed_phone = (EditText)findViewById(R.id.ed_phone);
        ed_pass = (EditText)findViewById(R.id.ed_pass);
        btn_regis = (Button)findViewById(R.id.btn_regis);
        btn_regis.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        String user = ed_phone.getText().toString().trim();
        String pass = ed_pass.getText().toString().trim();
        presenter1 = new LoginPresenter(this,RegisActivity.this);
        boolean b = presenter1.checkData(user, pass);
       if(b){
            presenter1.login("http://120.27.23.105/user/reg",user,pass);
        }
    }

    @Override
    public void succeed(String data) {
            finish();
    }

    @Override
    public void failed(String message) {
        //正常情況不會執行,只有請求網路失敗的時候才會執行,這裡的失敗是指網頁打不開的失敗
        Toast.makeText(RegisActivity.this,"網路異常,註冊失敗",Toast.LENGTH_SHORT).show();
    }

    //防止記憶體洩漏
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter1 != null) {
            presenter1.detatch();
        }
    }
}