MVP+Dagger2设计,MVP架构模式实现新思路 (Demo)

> MVP+Dagger2

-- Dagger2 是Google 的新一代依赖注入框架。

Android MVP使用Dagger2的sample代码- https://github.com/ChineseLincoln/Dagger2Mvp
将MVP,Dagger,Retrofit,Rxjava等技术相结合并用于快速开发的框架-https://github.com/JessYanCoding/MVPArms
MVP初体验与MVP引入Dagger2初体验- https://blog.csdn.net/shoushow_yeping/article/details/71421627

Dagger2+MVP的简单封装- https://blog.csdn.net/Sean_css/article/details/79624156

-- Dagger2 与 MVP 的结合(解耦和扩展以及团队协作),基本思路:
 1.全局Component通过AppComponent进行管理,大多设置单例模式;
 2.将Activity和Fragment Component中通用的抽出,为BaseViewComponent;

 3.上层PresenterComponent继承BaseViewComponet,DataBandingComponent只能继承android.databinding.DataBindingComponent,但可以将BaseViewModule 包含进来。其他Component使用时可以继承BaseViewComponent。

传统MVP用在项目中是真的方便还是累赘?-https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649548821&idx=1&sn=255a2c1552ddef3542c2648a29eea26e&chksm=f1180368c66f8a7e3c0d70902f07621dac48c28a1a8b30e3d90e0ae531c80b3325292306feca&scene=21#wechat_redirect
MVPArms- https://github.com/JessYanCoding/MVPArms/wiki
-- 总结一下MVP的缺点
1.粒度不好控制,控制不好就需要写过多的类和接口
2.如要重用presenter可能会实现过多不需要的接口
3.Presenter和View通过接口通信太繁琐,一旦View层需要的数据变化,那么对应的接口就需要
MVP需要创建太多的类和接口,并且每次通信都需要繁琐的通过接口传递信息
支付宝团队使用的T-MVP框架,是通过将Activity或Fragment作为Presenter,将UI操作抽到Delegate中,作为View层。

-- Dragger,控制反转;inject,依赖注入。
依赖注入的框架dragger2 和ButterKnife,控制反转(IOC:Inversion of Control))
依赖注入是实现控制反转的方式之一(另一方式是依赖查找),目的就是为了让调用者和被调用者之间解耦。
ButterKnife:https://github.com/JakeWharton/butterknife
Dagger2 主页: https://github.com/google/dagger

 控制反转:对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
 依赖注入:被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中。

  依赖注入有如下实现方式:
· 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
· 基于 set方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
· 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
· 基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。

 依赖查找:主动索取响应名称的对象,获得依赖对象的时间也可以在代码中自由控制。
依赖反转原则:
 1.高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
 2.抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。

> MVP (接口回调与解耦)

 -- MVP的实现方式很多,这里介绍两种:

1.以Activity和Fragment作为View(视图层),Presenter管理业务逻辑;
2.使用Activity和Fragment作为presenters,View单独抽出处理视图相关。
 -- 使用Activity和Fragment作为Presenters为何要采用这种方式呢,基于两点来考虑:
 Activity Fragment本身就有生命周期的管理,这种管理类似于业务逻辑,所以要归为Presenter;
 Activity Fragment生命周期变化时,会带来业务逻辑的变化,直接作为Presenter,可以避免业务逻辑的复杂。
  -- MVP Model 层主要的职责有:
1.从网络,数据库,文件,传感器,第三方等数据源读写数据。
2.对外部的数据类型进行解析转换为APP内部数据交由上层处理。
3.对数据的临时存储,管理,协调上层数据请求。
  View层,在 MVP 开发中,View 层通常指的是 Activity 、Fragment、View、ViewGroup 等。主要职责:
1.提供 UI 交互
2.在 Presenter 的控制下修改 UI。
3.将业务事件交由 Presenter 处理

  P层,Presenter 层主要是连接 View 层和 Model 层的桥梁,负责把 View 层需要数据从 Model 层拿到,返回给 View 层;

-- MVP 架构项目MinimalistWeather(GitHub),将网络库升级到了Retrofit2+OKHttp3 https://github.com/BaronZ88https://github.com/BaronZ88/MinimalistWeather,开源天气App,采用MVP+RxJava+Retrofit2+OKHttp3+Dagger2+RetroLambda等开源库来实现.

MVP的主要思想就是解耦View和Model。TheMVP- https://github.com/kymjs/TheMVP
适用于小型项目的 Android MVP 架构- https://github.com/0xZhangKe/AndroidMVP
 使小型项目也可以很自然的使用 MVP 架构,实现高内聚,低耦合。

一个demo项目https://github.com/wongcain/MVP-Simple-Demo

Android-CleanArchitecture(MVP)- https://github.com/android10/Android-CleanArchitecture

https://github.com/youxin11544/mvp_hybride_framwork (这是一个Android MVP模型良好的架构设计,同时也做了Android和HTML 5交互架构,用到了RxJava+Retrofit+MVP+泛型缩减mvp+模板模式+命令模式+观察者模式+管理者模式 +简单工厂模式。

-- MVP:
android-architecture- https://github.com/googlesamples/android-architecture/tree/master
Android-CleanArchitecture- https://github.com/android10/Android-CleanArchitecture
iosched- https://github.com/google/iosched

Android架构(一)MVP架构在Android中的实践- http://blog.csdn.net/johnny901114/article/details/54783106
 MVP的架构有如下好处:
1)降低了View和Model的耦合,通过Presenter层来通信;
2)把视图层抽象到View接口,逻辑层抽象到Presenter接口,提高了代码的可读性、可维护性;
3)Activity和Fragment功能变得更加单一,只需要处理View相关的逻辑;
4)Presenter抽象成接口,就可以有多种实现,方便单元测试。

MVP架构:

 View Layer: 只负责UI的绘制呈现,包含Fragment和一些自定义的UI组件,View层需要实现ViewInterface接口。Activity在项目中不再负责View的职责,仅仅是一个全局的控制者,负责创建View和Presenter的实例;
 Model Layer: 负责检索、存储、操作数据,包括来自网络、数据库、磁盘文件和SharedPreferences的数据;
 Presenter Layer: 作为View Layer和Module Layer的之间的纽带,它从model层中获取数据,然后调用View的接口去控制View;
 Contract: 我们参照Google的demo加入契约类Contract来统一管理View和Presenter的接口,使得某一功能模块的接口能更加直观的呈现出来,这样做是有利于后期维护的。

使用Activity和Fragment作为视图层(View)真的合适么?
    目前很多使用了MVP模式的android 项目,基本上都是将activity和fragment作为视图层来进行处理的.而presenters通常是通过继承自被视图层实例化或者注入的对象来得到的. 诚然,我同意说,这种方式可以节省掉很多让人厌烦的"import android.."语句, 并且将presenters从activity的生命周期中分割出来以后, 项目后续的维护会变得简便很多.这种思路是正确的, 但是,从另一个角度来说, activity 有一个很复杂的生命周期(fragment的生命周期可能会更复杂), 而这些生命周期很有可能对你项目的业务逻辑有非常重大的影响. Activity 可以获取上下文环境和多种android系统服务. Activity中发送Intent,启动Service和执行FragmentTransisitons等。而这些特性在我看来绝不应该是视图层应该涉及的领域(视图的功能就是现实数据和从用户那里获取输入数据,在理想的情况下,视图应该避免业务逻辑).

基于上述的原因,我对目前的主流做法并不赞同,所以我在尝试使用Activity和Fragment作为Presenters。

使用Activity和Fragment作为presenters的步骤:
1. 去除所有的view
  将Activity和Fragment作为presenter最大的困难就是如何将关于UI的逻辑抽取出来.我的解决方案是: 让需要作为presenter的activity 或者 fragment来继承一个抽象的类(或者叫"基类"), 这样关于View 各种组件的初始化以及逻辑,都可以在继承了抽象类的方法中进行操作,而当继承了该抽象类的class需要对某些组件进行操作的时候,只需要调用继承自抽象类的方法,就可以了。
  那么抽象类怎么获取到的view组件呢?在抽象类里面会有一个实例化的接口,这个接口里面的init()方法就会对view进行实例化,这个接口如下:
public interface Vu {
    void init(LayoutInflater inflater, ViewGroup container);
    View getView();
}
正如你所见,Vu定义了一个通用的初始化例程,我可以通过它来实现一个容器视图,它也有一个方法来获得一个View的实例,每一个presenter将会和它自己的Vu关联,这个presenter将会继承这个接口(直接或者间接的去继承一个来自Vu的接口)

2. 创建一个presenter基类 (Activity)
    有了Vu接口,我们可以通过构建一系列的class来操纵很多不同的view组件,这些class 使用Vu接口来初始化View组件,并通过继承的方式给子类以操纵view组件的方法,以此来达到将ui 逻辑剥离出activity的目的。在下面的代码中,你可以看到,我覆写了activity的onCreate 、 onCreateView、onDestroy 、 onDestroyView,通过对这些方法的覆写,就可以对Vu的实例化和销毁进行精确的控制(vu.init()就是实例化一个view组件)。onBindVu() 和onDestoryVu()是控制view生命周期的两个方法。通过对actiivty中相关方法的覆写达到控制组件的生命周期的目的(具体看下面的代码,你就明白了), 这样做的好处就是无论是activity 还是 fragment, 其用与控制view组件创建和销毁的语句是一样的(尽量避免定义多余的函数)。这样的话,二者之间的切换也会减少一定的阻力(也许你今天的需求是用fragment实现的,但是第二天发现使用fragment会有一个惊天bug,译者本人就遇到过)。

public abstract class BasePresenterActivity<V extends Vu> extends Activity {
    protected V vu;
    @Override
    protected final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            vu = getVuClass().newInstance();
            vu.init(getLayoutInflater(), null);
            setContentView(vu.getView());
            onBindVu();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected final void onDestroy() {
        onDestroyVu();
        vu = null;
        super.onDestroy();
    }

    protected abstract Class<V> getVuClass();
    protected void onBindVu(){};
    protected void onDestroyVu() {};
}
3. 创建一个基本的presenter(Fragment)
public abstract class BasePresenterFragment<V extends Vu> extends Fragment {
    protected V vu;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = null;
        try {
            vu = getVuClass().newInstance();
            vu.init(inflater, container);
            onBindVu();
            view = vu.getView();
        } catch (java.lang.InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return view;
    }
    @Override
    public final void onDestroyView() {
        onDestroyVu();
        vu = null;
        super.onDestroyView();
    }

    protected void onDestroyVu() {};
    protected void onBindVu(){};
    protected abstract Class<V> getVuClass();

}

 

4. 一个简单的例子
如前文所述,我们已经确定了一个框架,现在就来写一个简单的例子来进一步的说明. 为了避免篇幅过长,我就写一个“hello world”的例子。首先要有一个实现Vu接口的类:
public class HelloVu implements Vu {
View view;
TextView helloView;
@Override
public void init(LayoutInflater inflater, ViewGroup container) {
    view = inflater.inflate(R.layout.hello, container, false);
    helloView = (TextView) view.findViewById(R.id.hello);
}
@Override
public View getView() {
    return view;
}
public void setHelloMessage(String msg){
    helloView.setText(msg);
}

}

 

下一步,创建一个presenter来操作这个TextView
public class HelloActivity extends BasePresenterActivity {
@Override
protected void onBindVu() {
    vu.setHelloMessage("Hello World!");
}
@Override
protected Class<HelloVu> getVuClass() {
    return HelloVu.class;
}

}

OK,这样就大功告成了!!是不是很简便!
等等...耦合警告!

你可能注意到我的HelloVu类直接实现了Vu接口,我的Presenter的getVuClass方法直接引用了实现类。传统的MVP模式中,Presenter是要通过接口与他们的View解耦合的。因此,你也可以这么做。避免直接实现Vu接口,我们可以创建一个扩展了Vu的IHelloView接口,然后使用这个接口作为Presenter的泛型类型。这样Presenter看起来应该是如下这样的 :

public class HelloActivity extends BasePresenterActivity<IHelloVu> {
    @Override
    protected void onBindVu() {
        vu.setHelloMessage("Hello World!");
    }
    @Override
    protected Class<HelloVuImpl> getVuClass() {
        return HelloVuImpl.class;
    }
}
在我使用强大的模拟工具过程中,我个人并没有看到在一个接口下面实现Vu所带来的好处。但是对于我来说一个好的方面是,有没有Vu接口它都能够工作,唯一的需求就是最终你会实现Vu。

5. 如何进行测试
通过以上几步,我们可以发现,去除了UI逻辑之后,Activity变得非常简洁。并且,相关的测试 也变的非常异常的简单。请看如下的代码:
public class HelloActivityTest {
    HelloActivity activity;
    HelloVu vu;
    @Before
    public void setup() throws Exception {
        activity = new HelloActivity();
        vu = Mockito.mock(HelloVu.class);
        activity.vu = vu;
    }
    @Test
    public void testOnBindVu(){
        activity.onBindVu();
        verify(vu).setHelloMessage("Hello World!");
    }
    }

以上代码是一段标准的jnuit单元测试的代码,不需要在android设备中部署运行,只需要在编译环境中即可测试。大幅度的提高了测试效率。但是,在测试某些方法的时候,你必须要使用android设备,例如当你想测试activity生命周期中的resume()方法。在缺乏设备环境的时候,super.resume()会报错。为了解决这个问题,可以借鉴一些工具,例如Robolectric、还有android gradle 1.1 插件中内置的testOptions { unitTests.returnDefaultValues = true }。此外,你仍然可以将这些生命周期也抽离出来。例如如下:

@Override
protected final void onResume() {
    super.onResume();
    afterResume();
}

protected void afterResume(){}
现在,你可以在没有android设备的情况下,快速的测试了!
意外收获:使用adapter作为presenter
将Activity作为presente已经足够狡猾了吧?使用adapter作为presenter,你想过没有? 好吧,请看如下的代码:
public abstract class BasePresenterAdapter extends BaseAdapter {
protected V vu;
@Override
public final View getView(int position, View convertView, ViewGroup parent) {
    if(convertView == null) {
        LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        try {
            vu = (V) getVuClass().newInstance();
            vu.init(inflater, parent);
            convertView = vu.getView();
            convertView.setTag(vu);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    } else {
        vu = (V) convertView.getTag();
    }
    if(convertView!=null) {
        onBindListItemVu(position);
    }
    return convertView;
}
protected abstract void onBindListItemVu(int position);

protected abstract Class<V> getVuClass();

}
如上代码,使用adapter作为presenter其实和activity或者fragement几乎是一样的,只有一点明显的区别就是,我把onBingVu替换成了onBindListItemVu(接受int参数),其实我是借鉴了ViewHolder模式。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这里提供一个简单的 Android Dagger2 的案例 demo,使用 Dagger2 实现依赖注入,实现了一个简单的计算器功能。 首先,在 build.gradle 中添加依赖: ``` dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' } ``` 然后,定义依赖关系。在这个例子中,我们定义了两个依赖:Calculator 和 CalculatorModule。Calculator 是一个接口,CalculatorModule 是一个模块,其中包含了用于实例化 Calculator 对象的方法。 ``` public interface Calculator { int add(int a, int b); } @Module public class CalculatorModule { @Provides Calculator provideCalculator() { return new CalculatorImpl(); } } ``` 其中,CalculatorImpl 是 Calculator 接口的实现类,用于实现 add 方法。 ``` public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { return a + b; } } ``` 在 Activity 中,我们需要使用 Calculator 对象,可以通过 @Inject 注解实现依赖注入。 ``` public class MainActivity extends AppCompatActivity { @Inject Calculator calculator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerCalculatorComponent.create().inject(this); int result = calculator.add(1, 2); Toast.makeText(this, "1 + 2 = " + result, Toast.LENGTH_SHORT).show(); } } ``` 其中,DaggerCalculatorComponent 是 Dagger2 自动生成的 Component 类,用于创建 Calculator 对象,并注入到 MainActivity 中。 最后,我们需要定义一个接口,用于将 CalculatorModule 和 MainActivity 连接起来。 ``` @Component(modules = {CalculatorModule.class}) public interface CalculatorComponent { void inject(MainActivity activity); } ``` 以上就是 Android Dagger2 的一个简单 demo

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值