MVC 簡介
- MVC 模式代表 Model-View-Controller(模型-視圖-控制器) 模式。這種模式用於應用程序的分層開發。
- Model(模型) - 模型代表一個存取數據的對象或 JAVA POJO。它也可以帶有邏輯,在數據變化時更新控制器。
- View(視圖) - 視圖代表模型包含的數據的可視化。
- Controller(控制器) - 控制器作用於模型和視圖上。它控制數據流向模型對象,並在數據變化時更新視圖。它使視圖與模型分離開。
下面是一個簡單的 mvc 實現的例子
- MainActivity如下,就是簡單的一個ListView展示了個列表,適配器就不粘出來了
public class MainActivity extends BaseActivity {
private ListView listView;
private List<InfoBean> lists = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
getData();
}
private void init() {
listView = findViewById(R.id.listView);
}
private void getData() {
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336456530&di=d427f7650721a197f1dbe68169814608&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20200103%2Fcb1d64d373f54f21928a4813f41937e6.jpeg",
"語文書", "從小就學習的語文書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336510815&di=8f8dfc6f48f2864409205a1a5c8bda1b&imgtype=0&src=http%3A%2F%2Fimg14.360buyimg.com%2Fn1%2Fjfs%2Ft7012%2F299%2F121318139%2F28880%2F44c02f66%2F5972f6bfN3eee543b.jpg",
"數學書", "從小就學習的數學書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336548967&di=4bc1d03cd9a0e68d4b90740fcdd61bca&imgtype=0&src=http%3A%2F%2Fshopimg.kongfz.com.cn%2F20130326%2F118587%2F118587DE4vm0_b.jpg",
"英語書", "從小就學習的英語書"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=189950173,2182075783&fm=26&gp=0.jpg",
"化學書", "從小就學習的化學書"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2986308404,3738485014&fm=26&gp=0.jpg",
"物理書", "從小就學習的物理書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336997608&di=9ac209c043c6dba2300cf4df9cc1b554&imgtype=0&src=http%3A%2F%2Fimg.jk51.com%2Fimg_jk51%2F148284641.jpeg",
"生物書", "從小就學習的生物書"));
listView.setAdapter(new MyAdapter(this, lists));
}
}
效果圖如下:
- 以上是簡單的一個 mvc 實現的邏輯,view 和 data 綁定都放在了activity中,如果代碼邏輯比較複雜的頁面 將來維護activity頁面會非常麻煩,所以要想辦法簡化代碼,降低耦合度。
MVP 簡介和基本框架搭建
-
簡稱:MVP 全稱:Model-View-Presenter ;MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。
接下來我們按照圖中所展示的來一點點改造上面的代碼。 -
上面的圖所示 創建 view 接口
public interface IMainView {
// 返回data
void showData(List<InfoBean> infoBeans);
}
- 創建 IModel接口和 model實現類
public interface IModel {
// 通過回調注入的形式獲取數據
void loadInfo(OnLoadListener loadListener);
interface OnLoadListener {
void onComplete(List<InfoBean> infoBeans);
}
}
public class MainModel implements IModel {
// 通過接口獲取數據
@Override
public void loadInfo(OnLoadListener loadListener) {
List<InfoBean> lists = new ArrayList<>();
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336456530&di=d427f7650721a197f1dbe68169814608&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20200103%2Fcb1d64d373f54f21928a4813f41937e6.jpeg",
"語文書", "從小就學習的語文書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336510815&di=8f8dfc6f48f2864409205a1a5c8bda1b&imgtype=0&src=http%3A%2F%2Fimg14.360buyimg.com%2Fn1%2Fjfs%2Ft7012%2F299%2F121318139%2F28880%2F44c02f66%2F5972f6bfN3eee543b.jpg",
"數學書", "從小就學習的數學書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336548967&di=4bc1d03cd9a0e68d4b90740fcdd61bca&imgtype=0&src=http%3A%2F%2Fshopimg.kongfz.com.cn%2F20130326%2F118587%2F118587DE4vm0_b.jpg",
"英語書", "從小就學習的英語書"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=189950173,2182075783&fm=26&gp=0.jpg",
"化學書", "從小就學習的化學書"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2986308404,3738485014&fm=26&gp=0.jpg",
"物理書", "從小就學習的物理書"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336997608&di=9ac209c043c6dba2300cf4df9cc1b554&imgtype=0&src=http%3A%2F%2Fimg.jk51.com%2Fimg_jk51%2F148284641.jpeg",
"生物書", "從小就學習的生物書"));
loadListener.onComplete(lists);
}
}
- 創建 Presenter
public class MainPresenter {
// 依賴 view
private IMainView iMainView;
// 依賴 model
private IModel mainModel = new MainModel();
public MainPresenter(IMainView iMainView) {
this.iMainView = iMainView;
}
// 通過model 獲取數據 返回回調
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
// 通過 view 返回數據
iMainView.showData(infoBeans);
}
});
}
}
- view層代碼
// 實現 IMainView接口
public class MainActivity extends BaseActivity implements IMainView {
private ListView listView;
private MainPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
// 初始化 Presenter
presenter = new MainPresenter(this);
// 獲取數據
presenter.fetch();
}
private void init() {
listView = findViewById(R.id.listView);
}
@Override
public void showData(List<InfoBean> infoBeans) {
// 設置回調數據結果
listView.setAdapter(new MyAdapter(this, infoBeans));
}
}
以上就是一個基本的 mvp 架構。但是基本的架構首先不滿足擴展,不能動態注入。並且當 Presenter有網絡請求的時候,Activity銷燬後不能及時回收,導致內存泄漏。接下來進行 mvp的重構。
MVP 重構(1)
- 防止內存泄漏
- 解決方案1是傳入P層的View採用若引用方式。
public class MainPresenter<T extends IMainView> {
private WeakReference<T> iMainView;
private IModel mainModel = new MainModel();
public MainPresenter(T iMainView) {
this.iMainView = new WeakReference<T>(iMainView);
}
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
iMainView.get().showData(infoBeans);
}
});
}
}
- 採用管理生命週期的方式防止內存泄漏 presenter中去掉構造方法
public class MainPresenter<T extends IMainView> {
private WeakReference<T> iMainView;
private IModel mainModel = new MainModel();
/**
* 綁定view
*/
public void attachView(T view) {
this.iMainView = new WeakReference<T>(view);
}
/**
* 解綁view
*/
public void detachView() {
iMainView.clear();
iMainView = null;
}
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
iMainView.get().showData(infoBeans);
}
});
}
}
上面的代碼不能每次使用的時候都需要寫這麼多的代碼,怎麼簡化呢接下來繼續抽離。
MVP 重構(2)
抽離到base。
- BaseActiity
// T 傳入 Presenter V 傳入View
public abstract class BaseActivity<T extends BasePresenter, V> extends AppCompatActivity {
protected T presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
if (presenter!=null){
// 綁定View
presenter.attachView((V) this);
}
}
// 子類創建 presenter
protected abstract T createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
// 解綁view
presenter.detachView();
}
}
- BasePresenter
/**
* @param <V> 傳入View
*/
public class BasePresenter<V> {
private WeakReference<V> referenceView;
V getView() {
if (referenceView != null) {
return referenceView.get();
}
return null;
}
public void attachView(V view) {
this.referenceView = new WeakReference<V>(view);
}
public void detachView() {
if (referenceView != null) {
referenceView.clear();
referenceView = null;
}
}
}
- 具體的Presenter
public class MainPresenter<T extends IMainView> extends BasePresenter<T> {
private IModel mainModel = new MainModel();
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
getView().showData(infoBeans);
}
});
}
}
- MainActivity 使用
public class MainActivity extends BaseActivity<MainPresenter<IMainView>,IMainView> implements IMainView {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
presenter.fetch();
}
@Override
public MainPresenter<IMainView> createPresenter() {
return new MainPresenter<>();
}
private void init() {
listView = findViewById(R.id.listView);
}
@Override
public void showData(List<InfoBean> infoBeans) {
listView.setAdapter(new MyAdapter(this, infoBeans));
}
}
以上就是 MVP 的基本框架了。有個性需求可以自己擴展。比如傳入多個 Presenter等
以上最終代碼下載請點擊
對網絡,圖片等的封裝
-
新建一個 commonLib 的module。
-
新建一個 IHttpProcessor 接口,定義需要的訪問類型接口。
/**
* 各種訪問類型接口
*/
public interface IHttpProcessor {
/**
* 網絡操作
*/
void post(String url, Map<String,Object> params, ICallback callback);
void delete(String url, Map<String,Object> params, ICallback callback);
}
- 新建 Helper 做初始化和調用工作
/**
* Http 請求初始化類
*/
public class HttpHelper {
private static IHttpProcessor processor;
private HttpHelper() {
}
public static HttpHelper getInstance() {
return HttpHelperSingleTon.httpHelper;
}
private static class HttpHelperSingleTon {
private static HttpHelper httpHelper = new HttpHelper();
}
public void init(IHttpProcessor processor) {
HttpHelper.processor = processor;
}
// 調用 post 操作
public void post(String url, Map<String, Object> params, ICallback callback) {
// 請求代理給 具體的 processor 執行具體訪問
// appendParams 是將 post可以轉爲 get請求 訪問get請求的時候也可以使用此post方法
String finalUrl = appendParams(url, params);
processor.post(finalUrl, params, callback);
}
// 調用 delete 操作
public void delete(String url, Map<String, Object> params, ICallback callback) {
processor.delete(url, params, callback);
}
private static String appendParams(String url, Map<String, Object> params) {
if (params == null || params.isEmpty()) {
return url;
}
StringBuilder urlBuilder = new StringBuilder(url);
if (urlBuilder.indexOf("?") <= 0) {
urlBuilder.append("?");
} else {
if (!urlBuilder.toString().endsWith("?")) {
urlBuilder.append("&");
}
}
for (Map.Entry<String, Object> entry : params.entrySet()) {
urlBuilder.append("&" + entry.getKey())
.append("=")
.append(encode(entry.getValue().toString()));
}
return urlBuilder.toString();
}
private static String encode(String str) {
try {
return URLEncoder.encode(str, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
- 新建 ICalback
public interface ICallback {
void onSuccess(String result);
void onError(String result);
}
- 新建解析 callBack數據的具體callback
/**
* 回調接口的json版本的實現類
* 用於把網絡返回的json字符串轉讓換成對象(Result就是用戶接收數據的類型)
*/
public abstract class HttpCallback<Result> implements ICallback {
@Override
public void onSuccess(String result) {//result就是網絡回來的數據
//result把轉換成用戶需要的對象
Gson gson=new Gson();
//需要得到用戶輸入的對象對應的字節碼是什麼樣的
//得到用戶接收數據的對象對應的class
Class<?> clz=analysisClassInfo(this);
Result objResult=(Result)gson.fromJson(result,clz);
//回調給調用層
this.onSuccess(objResult);
}
public abstract void onSuccess(Result result);
/**
* 獲取反省<>中的類型
* @param object
* @return
*/
private Class<?> analysisClassInfo(Object object) {
//getGenericSuperclass可以得到包含原始類型,參數化類型,數組,類型變量,基本數據
Type genType=object.getClass().getGenericSuperclass();
//獲取參數化類型
Type[] params=((ParameterizedType)genType).getActualTypeArguments();
return (Class<?>)params[0];
}
@Override
public void onError(String result) {
}
}
- 新建具體的網絡訪問 如 okHttp volley 等
public class OkHttpProcessor implements IHttpProcessor {
private OkHttpClient mOkHttpClient;
private Handler myHandler;
public OkHttpProcessor() {
mOkHttpClient = new OkHttpClient();
myHandler = new Handler();
}
private RequestBody appendBody(Map<String, Object> params) {
FormBody.Builder body = new FormBody.Builder();
if (params == null || params.isEmpty()) {
return body.build();
}
for (Map.Entry<String, Object> entry : params.entrySet()) {
body.add(entry.getKey(), entry.getValue().toString());
}
return body.build();
}
@Override
public void post(String url, Map<String, Object> params, final ICallback callback) {
RequestBody requestBody = appendBody(params);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onError(e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
if (response.isSuccessful()) {
myHandler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(result);
}
});
}
}
});
}
@Override
public void delete(String url, Map<String, Object> params, ICallback callback) {
}
}
- 使用:app添加對 commonLib 的依賴在 app 中的 applicaiton 中初始化操作
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 傳入具體的實現
HttpHelper.getInstance().init(new OkHttpProcessor());
// HttpHelper.getInstance().init(new VolleyProcessor(this));
}
}
- 在 mvp的 model層調用訪問
public class MainModel implements IModel {
@Override
public void loadInfo(OnLoadListener loadListener) {
//測試隔離層代碼
String url="url";
HashMap<String,Object> params=new HashMap<>();
params.put("param","");
HttpHelper.getInstance().post(url, params, new HttpCallback<TestBean>() {
@Override
public void onSuccess(TestBean testBean) {
Log.e("onSuccess",testBean.toString());
}
@Override
public void onError(String result) {
}
});
}
}
使用這種方式的好處是當想切換新的框架時,不需要改業務邏輯的代碼,只需要在 application 初始化的時候切換一下新增的 processor 實現就可以了。同樣圖片框架,數據庫,都可以使用這種方式來切換。
源碼已經上傳有需要的可以下載自己完善 跳轉連接