关于MVP+Dagger2的一些知识
看了大神Android-从零开始搭建android框架系列 前几章还比较清晰 到了dagger2发现了dagger2的优点 但是+mvp就不是特别理解如何在项目中运用。
文章评论中推荐了另一个作者的文章
需要找时间把这三篇文章看明白 然后再修改mvp模式的demo
笔记1.
依赖注入(Dependency Injection简称DI)
java中 注解(Annotation)
Inject
优点:取消new对象通过 注入 获取实例
注解(Annotation)来标注目标类中所依赖的其他类,同样用注解来标注所依赖的其他类的构造函数,那注解的名字就叫Inject
Component
一个连接两个类的桥梁
需要引用到目标类的实例,查找用Inject注解标注的属性,与对应的构造函数并赋值,Component也叫注入器(Injector)
Module
封装第三方类库使用,简单的工厂模式。
Component中的modules属性可以把Module加入Component,modules可以加入多个Module。
Provides
Module中的创建类实例方法用Provides进行标注,Component在搜索到目标类中用Inject注解标注的属性后,Component就会去Module中去查找用Provides标注的对应的创建类实例方法,这样就可以解决第三方类库用dagger2实现依赖注入了。
笔记2
Qualifier(限定符)
通过Inject创建实体类和通过Module创建实体类创建一个类时,依赖注入迷失,用限定符对不同创建实例方法标识,对目标类响应的实例属性进行标注,能解决问题。
Scope(作用域)
通过自定义Scope注解可以更好的管理创建的类实例的生命周期。
划分Component有两种,一种针对全局,最终针对activity或者fragment
Singleton
(在Module中定义创建全局类实例的方法
ApplicationComponent管理Module
保证ApplicationComponent只有一个实例)
更好的管理ApplicationComponent和Module之间的关系,保证ApplicationComponent和Module是匹配的。若ApplicationComponent和Module的Scope是不一样的,则在编译时报错。
组织Component
类实例共享三种方式
依赖方式:Component中的dependencies属性
包含方式:SubComponent就是包含方式的具体实现
继承方式:把一些Component共有的方法抽象到一个父类中,然后子Component继承
Scope
更好的管理Component之间的组织方式,更好的管理Component与Module之间的匹配关系,可读性提高,如用Singleton标注全局类,这样让程序猿立马就能明白这类是全局单例类。
笔记3
dagger2的好处
增加开发效率,省去重复的简单体力劳动。
更好的管理类实例。
解耦
步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
看构造函数是否存在参数
步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
`
注意事项
1.一个app必须要有一个Component(名字可以是ApplicationComponent)用来管理app的整个全局类实例
2.多个页面可以共享一个Component
3.不是说Component就一定要对应一个或多个Module,Component也可以不包含Module
4.自定义Scope注解最好使用上,虽然不使用也是可以让项目运行起来的,但是加上好处多多。
接下来分析一下大神的Demo
首先看一下项目结构
结构1.png 结构2.png
这里边有个有趣的事情
App这个类中DaggerAppComponent 是报错的 没有找到这类,但是项目编译后已经导入不报错了,点开文件发现文件在debug文件夹内
结构3.png
这个文件位置在上边,这种情况我是没有用到过的,发现这个类被 @Generated 标记了,也许和这个标注有关。
Demo运行起来很简单 两个按钮 一个获取用户信息,一个点击显示toast
首先分析一下data包
GetUserData构造方法被 @Inject标注了,其中还有getUser()方法,返回的是一个UserData的实例,name属性有值。
UserData是一个实体类只有一个name属性。
di包就是dagger2用到相关的类
components包连接两个类的桥梁
ActivityComponent 是同时被Component和scopes.PerActivity标注的里边有一个getActivity()方法。
AppComponent有两个标记@Singleton是单例的意思,@Component是连接器的意思,里边有3个方法,获取上下文,获取toast工具,获取Navigator。
MainComponent有两个标记@PerActivity和@Component并继承了ActivityComponent。里边有两个方法inject(mainActivity)方法和获取mainFragmentComponent实例的方法。
MainFragmentComponent类中有两个标记@PerActivity和@Subcomponent,还有一个inject(mainFragment)方法。
modules封装第三方类库使用,简单的工厂模式。
AppModule被@Module标记,里边有个三个方法provideContext,provideNavigator,provideToastUtil都被 @Provides 和@Singleton标记。
ActivityModule也被@Module标记,在构造方法中传入了activity实例,并通过被@Provides 和 @PerActivity 标记的provideActivity方法return出去。
MainModule也被@Module标记,里边只有1个@Provides标记的provideUserData()方法
scopes作用域包只有被@Scope和@Retention标记的PerActivity接口。
presenter包下只有MainPresenter这个类,里边有一个静态接口IUserView和它的set方法与实例。它的构造方法被@Inject标记并传入了GetUserData的实例,通过getUser方法给接口传递了数据
view包下 BaseActivity 有1个getAppComponent方法,BaseFragment没有方法。
MainActivity实现了MainFragment中的一个监听接口,通过DaggerMainComponent.builder()方法获取了ActivityModule的对象并调用了inject方法,提供了getMainComponent方法 return这个对象。
mainFragment这个类中通过@Inject 获取了mainPresenter、toastUtil、multiConstruct三个实例在onActivityCreated方法中得到MainFragmentComponent对象并实现inject方法,通过mainPresenter的setUserView 设置了数据,在onCreateView的两个点击事件中通过mainPresenter获取了数据,通过toastUtil展示了toast.
在包的最外层App的onCreate中获取了AppModule对象并提供了return方法。
MultiConstruct类中 @Inject了构造函数
Navigator被@Singleton标记了。
ToastUtil封装了展示吐司的方法。
总结一下:
di包的components包中都是注入器,有app注入器、activity注入器、main注入器、fragment注入器。main注入器继承了activity注入器,对MainActivity进行依赖注入。
Module是一个提供类实例的类,通过标注关联了2个实例MainModule和ActivityModule,加入main注入器,他们的方法都被@Provides标注了。
当module中的类需要创建实例的时候需要用@PerActivity标记出来。
MainComponent继承了ActivityComponent并采用dependencies标记依赖了 AppComponent。
MainFragmentComponent通过@PerActivity与MainComponent声明了相同的作用域并通过@Subcomponent声明被包含的。
工厂模式也可以降低耦合度。
依赖注入:从容器中查找合适的对象,对当前的对象进行赋值。
被@Component标注类为注入器,bean生成工厂类,运行后在apt-debug包下。
注入器不能直接new 只能通过创建器进行创建。
@Inject只能针对无参构造,可以修改源代码。
@Module与@Provides 针对有参构造和jar包使用。对工厂模式类进行@Module,对实例类@Provides 方法用Provides 开头。在component类中标注出module的类,inject方法查找界面@inject的类进行赋值。调用时需多指定创建实例的工厂。
获取注入器 需要编译 生成dagger开头的类