大家好,我是小新
https://github.com/racofix
MVP,顧名思義:
View 對應于Activity,fragment,負責View的繪制以及與用戶交互
Model 依然是業務邏輯和實體模型
Presenter 負責完成View于Model間的交互
優點我就不介紹了,缺點可能都我一樣的困擾:
- M/P 層都需要定義接口和實現類,創建對象占用內存
- 每一個頁面都需要創建P實例并綁定View(重復代碼)
- P層邏輯比較多的時候,開發時不容易考慮其他頁面調用,復用難
- 頁面銷毀,P層執行異步操作,持有的View 引用造成內存泄露,程序崩潰
我們既然知道它的缺點了,那我們就想辦法解決
- 減少接口和類的定義,數據模型定義為Model,數據復雜的情況引入 Repository
- 利用用注解和反射,自動實現 P層實例化和View綁定/解綁
- P層邏輯范圍減少,頁面(Activity/Fragment)可以多次復用,低耦合高復用
- 生成代理View,頁面銷毀不需要 getView()!=null 并不會造成內存泄露
Usage
Talk is cheap. Show me the code.
P層(邏輯)
public interface LoginI {
interface Logic {
void sign_in(String username, String password);
}
interface View {
void sign_in_success();
}
}
public class Login extends BaseLogicImpl<LoginI.View> implements LoginI.Logic {
@Override
public void sign_in(String username, String password) {
if(successfully) getView().sign_in_success();
}
}
V層(Activity/Fragment)
獲取P實例 getLogic(Login.class)
縮小邏輯層的方法范圍,降低到最小力度,比如說:登錄頁需要登錄和注冊功能,注冊頁需要注冊功能。
那么登錄和注冊就可以作為邏輯層來實現,那登錄頁實現 登錄/注冊邏輯,注冊頁實現注冊邏輯。
@LogicArr({Login.class, Register.class})
public class LoginActivity extends BaseLogicActivity implements
LoginI.View, RegisterI.View {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLogic(Login.class).sign_in("用戶名", "密碼");
}
@Override
public void sign_in_success() {
Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
}
@Override
public void sign_up_success() {
Toast.makeText(this, "注冊成功", Toast.LENGTH_SHORT).show();
}
}
@LogicArr(Register.class)
public class RegisterActivity extends BaseLogicActivity
implements RegisterI.View {
@Override
public void sign_up_success() {
Toast.makeText(this, "注冊成功", Toast.LENGTH_SHORT).show();
}
}
這樣,就實現一個使用簡單,結構清晰的MVP結構。
解決這些缺點思考了很久,如果你覺得新穎、實用、可以幫到您的話,給一份關注和鼓勵。
同樣,歡迎各位大神相互指導改進。準備做一些常用的框架 Things2
更多示例:https://github.com/racofix/Basic
框架實現:https://github.com/Things2/Logic-x
設計思路
核心類 LogicProvider
利用注解 @LogicArr 返回邏輯數組類,利用反射初始化邏輯類,然后綁定/解綁View放入Map中
上層通過 getLogic(xxx.class) 從Map中獲取邏輯實例,頁面銷毀邏輯實例會自動從Map中釋放
class LogicProvider {
private static volatile LogicProvider logicProvider;
private Map<Object, HashSet<BaseLogic>> logicCaches = new LinkedHashMap<>();
static LogicProvider getInstance() {
if (logicProvider == null) {
synchronized (LogicProvider.class) {
if (logicProvider == null) {
logicProvider = new LogicProvider();
}
}
}
return logicProvider;
}
<V> void put(V view) {
LogicArr logicArr = view.getClass().getAnnotation(LogicArr.class);
if (logicArr == null) return;
if (logicCaches.containsKey(view)) return;
HashSet<BaseLogic> logics = new HashSet<>();
Class[] classes = logicArr.value();
for (Class clazz : classes) {
try {
BaseLogic<V> baseLogic = Util.castTo(clazz.newInstance());
baseLogic.bindView(view);
baseLogic.onLogicCreated();
logics.add(baseLogic);
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
if (logics.isEmpty()) return;
logicCaches.put(view, logics);
}
<V, T extends BaseLogic> T get(V view, Class<T> clazz) {
HashSet<BaseLogic> logics = logicCaches.get(view);
if (logics == null || logics.isEmpty()) {
throw new NullPointerException(view.getClass().getName() + " @LogicArr is empty.");
}
T baseLogic = null;
for (BaseLogic logic : logics) {
String logicName = logic.getClass().getName();
if (logicName.equals(clazz.getName())) {
baseLogic = Util.castTo(logic);
break;
}
}
return baseLogic;
}
<V> void remove(V view) {
HashSet<BaseLogic> logics = logicCaches.get(view);
if (logics == null || logics.isEmpty()) {
return;
}
for (BaseLogic logic : logics) {
if (logic.isViewBind()) logic.unbindView();
logic.onLogicDestroy();
}
logicCaches.remove(view);
}
}
核心類 BaseLogicImpl
利用WeakReference存儲View,便于View對象回收,使用動態代理 bindView(view) 生成代理View,
頁面銷毀時,代理對象還存在,調用方法時候發現View是空了, 代理對象就什么都不做了. 這樣既不用判斷 getView()!=null 并且不會內存泄露。
public class BaseLogicImpl<V> implements BaseLogic<V> {
private V viewProxy;
private WeakReference<V> weakReference;
@Override
public void bindView(final V view) {
weakReference = new WeakReference<>(view);
viewProxy = Util.castTo(
Proxy.newProxyInstance(
view.getClass().getClassLoader(),
view.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isViewBind()){
return method.invoke(weakReference.get(), args);
}
return null;
}
}));
}
@Override
public void unbindView() {
if (isViewBind()) {
weakReference.clear();
weakReference = null;
}
}
@Override
public boolean isViewBind() {
return weakReference != null && weakReference.get() != null;
}
@Override
public V getView() {
return viewProxy;
}
}
最后
- 項目示例代碼 (https://github.com/racofix/Basic)
- 開源框架Things2-Logic-x (https://github.com/Things2/Logic-x)
希望利用業余時間和工作的積累,貢獻更多有意義、有價值的項目。
如果有待改進,請大神們多多指導。
感謝大家耐心的閱讀,如果項目對你有所幫助,希望大家給個關注,大家一起進步。