架构师之路

6.绝绝子--Android - 抖音使用Dagger2实现MV

2021-12-28  本文已影响0人  鹏城十八少

dagger的用途就是:让你不需要初始化对象。换句话说,任何对象声明完了就能直接用。

依赖注入的好处

重用 Car,依赖通过外部传入过来!

`* 重用类以及分离依赖项:更容易换掉依赖项的实现。由于控制反转,代码重用得以改进,并且类不再控制其依赖项的创建方式,而是支持任何配置。

目的,改造演变:

第一种:直接new ,很多个地方new

第二种:统一new ,通过工厂的方式new,以后只改一个地方就可以

第三种:统一的地方,自动new

如何解藕?

1.MVP + Dagger2

专为MVP模式中的P层和V层做进一步解耦

因为在MVP模式中Activity持有presenter的引用,同时presenter也持有view的引用,这样便于更新UI界面,这样Activity就和 presenter仅仅的耦合在一起了,而Dagger2是依赖注入框架就是解耦合的,所以子MVP中使用Dagger2也就再好不过了。

activity持有了presenter的引用并且创建了该对象,但是如果presenter的构造函数发生改变则这里也需要改变,其实所有和presenter构造函数相关的代码都要改变。 

把很多对象new的地方统一起来了。之前用的是工厂模式的方式!工厂比直接new好一点,dagger2是自动注册,自动手写了很多new

我的理解:

1).MVP中 present还是会有view的依赖!

                如果很多个地方使用这个presenter。只要改一个地方就可以了,不用改多个地方。

2). 另外一种情况:

     A里面有B, B里面有c,C里面有D。

如果都手动new ,如果后面改起来,就会很麻烦!

2.dagger2+组件化


一:Dagger2是什么?

是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理

优点:

其目的是为了降低程序耦合。

随即而来的就是可测试性,可维护,可扩展性就大大提高了

很多小白对于Dagger2是啥浑然不知,更不知其能带来的好处了。

这里举个例子,比如有个类A,他的构造函数需要传入B,C;然后代码里有10个地方实例化了A,那如果功能更改,A的构造函数改成了只有B,这个时候,你是不是要去这10个地方一个一个的改?如果是100个地方,你是不是要吐血?!如果采用dagger2,这样的需求只需要改1-2个地方,你感觉怎么样?对你有诱惑吗?

原理:

这类依赖注入框架都已经采用了apt代码自动生成技术,其注解是停留在编译时,完全不影响性能。

dagger是使用依赖注入的方式,使用Annotation给需要注入的对象做标记,通过inject()方法自动注入所有对象,从而完成自动的初始化

dagger2 解决什么问题

第一:dagger 是一个依赖注入框架,首要任务当然是解决依赖注入的问题。
第二:dagger主要想通过编译时产生代码的方式来解决那些基于反射的依赖注入框架所存在的缺点,例如性能问题,开发过程中存在的问题。

注入的形式:

<pre style="margin: 8px 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: 宋体; font-size: 0.8rem;">public class MainActivity extends AppCompatActivity {

@Named("dev")
@Inject
MainApi apiDev;    @Named("release")
@Inject
MainApi apiRelease;    @Override

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.mainChildModule(new MainChildModule())
.build()
.inject(this);
apiDev.eat();
apiRelease.eat();
Log.i("TAG","apiDev--->" + apiDev);
Log.i("TAG","apiRelease--->" + apiRelease);
}</pre>

3要素:Inject Component Module

@Inject

在A类中标记B类的字段:告诉dagger 我们要注入B类的实例到A类中,你帮我搞定。

在B类中标记B类的构造函数:告诉dagger B类是一个可以被注入的类,如果你想把B的实例对象注入到其他类中,例如注入到A类中,是没有任何问题的。

@Component :中间桥接,对外接口

一般是一个使用 @Component标记的接口,其持有A类的实例,其在A类中发现有使用@Inject标记的属性b

@Module和 providers绑定在一起

C类是一个没有使用@Inject注解其构造函数的类

@Module注解表示,这个类是一个Module,Module的作用是提供信息,让ObjectGraph知道应该怎样注入所有的依赖

3、Component依赖Component

1、最简单不带ModuleInject方式

由我们自己定义的类,我们可以自由修改的情况下我们使用这种方式,也分为两种:带参数和不带参数的。

a、构造参数不带参数的:

例子:

b、构造参数带参数的:

构造函数如果带了inject注入的话,类就默认被注入了,不用像无惨的一样

例子:

发现Factory的构造函数被@Inject标注了且带有一个参数,

然后dagger2就去寻找Product发现它的构造函数也被@Inject标注并且无参数,于是dagger2把Product的实例注入给FactoryActivity

2、带ModuleInject方式

应用场景:若是我们引入的第三方库不能随意改动代码的话就不方便了,我们这里使用如下RetrofitManager模拟不可改动代码的情况:

对于这样的情况,可以使用@Provides注解来提供专用的初始化方法,实现自定义依赖。

@Provides
Coder provideCoder(Boss boss) {
    return new Coder(boss);
}

include的用法

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: 宋体; font-size: 0.8rem;">@Module (includes = {BModule.class})// includes 引入) public class AModule {
@Provides
A providerA() {
return new A();
}
}</pre>

@Named注解用

相当于有个表示,虽然大家都是同一个对象,但是实例化对象不同就不如

 A a1 = new A();    A a2 = new A();// a1  a2 能一样嘛

Module中 使用@Named注解

@Module
public class MainModule {

    private MainActivity activity;

    public MainModule(MainActivity activity) {
        this.activity = activity;
    }

    @Named("dev")
    @Provides
    MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"dev");
    }

    @Named("release")
    @Provides
    MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"release");
    }

}来源: https://www.jianshu.com/p/2cd491f0da01

在Activity/Fragment中使用

<pre class="hljs java" style="margin: 8px 0px;">

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: 宋体; font-size: 0.8rem;">public class MainActivity extends AppCompatActivity {

@Named("dev")
@Inject
MainApi apiDev;    @Named("release")
@Inject
MainApi apiRelease;    @Override

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.mainChildModule(new MainChildModule())
.build()
.inject(this);
apiDev.eat();
apiRelease.eat();
Log.i("TAG","apiDev--->" + apiDev);
Log.i("TAG","apiRelease--->" + apiRelease);
}

}</pre>

</pre>

<pre class="hljs java" style="margin: 8px 0px;">在项目中的应用场景和实例:
</pre>

<pre class="hljs java" style="margin: 8px 0px;">https://blog.csdn.net/soslinken/article/details/52184113</pre>

注意:

实质上,Dagger会在编译时对代码进行检查,并在检查不通过的时候报编译错误(为什么?这和Dagger的原理有关,有兴趣的话可以关注我之后发布的Dagger详解)。检查内容主要有三点:

  1. 所有含有依赖注入的类,需要被显式 声明在相应的Module中。
  2. 一个Module中所有@Provides方法的参数都必须在这个Module种提供相应的@Provides方法,或者在@Module注解后添加“complete = false”注明这是一个不完整Module(即它会被其他Module所扩展)。
  3. 一个Module中所有的@Provides方法都要被它声明的注入对象所使用,或者在@Module注解后添加“library = ture”注明(即它是为了扩展其他Module而存在的)。

@Binds:的使用。

————————————————

总结:

1.作用域--------重点,比较难的地方!

2.组件依赖

3.子组件 subConponent

爬坑指南(极度重要)

  1. Provide 如果是单例模式 对应的Compnent 也要是单例模式
  2. inject(Activity act) 不能放父类
  3. 即使使用了单利模式,在不同的Activity 对象还是不一样的
  4. 依赖component, component之间的Scoped 不能相同
  5. 子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application
  6. 多个Moudle 之间不能提供相同的对象实例
  7. Moudle 中使用了自定义的Scoped 那么对应的Compnent 使用同样的Scoped

重点是:处理作用域provider

通过APT+javapoAT在编译时候生成上面3个类!

APT的process()方法是什么时候执行?

原理分析:

1.a是怎么实例化的

2.b和c是怎么是咧化的

3.他们是怎么绑定的

原理:在build里面生成了很多的类,工厂模式,代理模式,A_Factory ,proxyProviderC

单列如何实现?

保持conponent是全局唯一的,通过application中初始化!

Dragger的原理:

[图片上传失败...(image-115c54-1640679669730)]

1.DaggerPengConponnet------注入和对象的关系

在里面,通过工厂模式得到provider

就是下面要用的2个类PengModule_GetHttpObjectFactory和MainActivity_MembersInjector

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public final class DaggerPengConponnet implements PengConponnet {
private Provider<HttpObject> getHttpObjectProvider; private MembersInjector<MainActivity> mainActivityMembersInjector; private DaggerPengConponnet(Builder builder) {
assert builder != null;
initialize(builder);
}

public static Builder builder() {
return new Builder();
}

public static PengConponnet create() {
return new Builder().build();
}

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {

this.getHttpObjectProvider = PengModule_GetHttpObjectFactory.create(builder.pengModule);   this.mainActivityMembersInjector = MainActivity_MembersInjector.create(getHttpObjectProvider);

}

@Override
public void injectActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}

public static final class Builder {
private PengModule pengModule; private Builder() {}

public PengConponnet build() {
  if (pengModule == null) {
    this.pengModule = new PengModule();

}
return new DaggerPengConponnet(this);
}

public Builder pengModule(PengModule pengModule) {
  this.pengModule = Preconditions.checkNotNull(pengModule);

return this; }
}
}
</pre>

2.PengModule_GetHttpObjectFactory --------工厂,new出对象。

通过工厂模式创建对象

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public final class PengModule_GetHttpObjectFactory implements Factory<HttpObject> {
private final PengModule module; public PengModule_GetHttpObjectFactory(PengModule module) {
assert module != null;
this.module = module;
}

@Override
public HttpObject get() {
return Preconditions.checkNotNull(
module.getHttpObject(), "Cannot return null from a non-@Nullable @Provides method");
}

public static Factory<HttpObject> create(PengModule module) {
return new PengModule_GetHttpObjectFactory(module);
}
}3.MainActivity_MembersInjector -----赋值</pre>

通过provider得到了需要注入的对象

3.MainActivity_MembersInjector:注入绑定

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<HttpObject> httpObjectProvider; public MainActivity_MembersInjector(Provider<HttpObject> httpObjectProvider) {
assert httpObjectProvider != null;
this.httpObjectProvider = httpObjectProvider;
}

public static MembersInjector<MainActivity> create(Provider<HttpObject> httpObjectProvider) {
return new MainActivity_MembersInjector(httpObjectProvider);
}

@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.httpObject = httpObjectProvider.get();
}

public static void injectHttpObject(
MainActivity instance, Provider<HttpObject> httpObjectProvider) {
instance.httpObject = httpObjectProvider.get();
}
}</pre>

20211227最新使用:

[图片上传失败...(image-cef848-1640679669730)]

使用4部曲

现在用Dagger2来改造,总体来说就是4步:

在操作中会使用到了@Inject、@Module、@Provides、@Conponent注解,那么他们分别在完成什么工作?

MainActivityPresenter presenter = new MainActivityPresenter(this);

@Inject @Conponent @Module @Provides

这四个注解可以这么理解,@Inject承担了=号左边的工作,@Conponent 承担了=号的工作, @Module @Provides承担了=号右边的工作

自己的步骤理解:

1.本来就有的bean对象获取其他对象httpObject

2.定义module:用来装载上面的object,一个module可以有多个object

3.定义组件component: 用来装module,一个component可以有多个module,一般是一个module.

4.使用,在activty中,直接定义httpobject,然后oncreate通过注解注入就可以了。

具体代码:

[图片上传失败...(image-8e9472-1640679669730)]

第一步: Bean

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class HttpObject {
}
</pre>

第二步:Module

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@Module public class PengModule {

@Provides

public HttpObject getHttpObject() {
return new HttpObject();
}

@Provides

public DatabaseObject getDatabase() {
return new DatabaseObject();
}

}
</pre>

第三步:Componet

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@Component(modules = PengModule.class) //可以有多个module public interface PengConponnet { // 接口给外部调用
void injectActivity(MainActivity mainActivity); }
</pre>

第四步:使用,需要用自动生成的类

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class MainActivity extends AppCompatActivity {

@Inject

HttpObject httpObject; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //自动生成类进行注入
DaggerPengConponnet.create().injectActivity(this);
}
}
</pre>

非常不错:

https://www.jianshu.com/p/2cd491f0da01

上一篇下一篇

猜你喜欢

热点阅读