• 提前准备
  • 参考文章
  • 环境配置
  • 开始撸代码
    • 好了上代码

      • 首先建立最大的 AppComponent
      • 写一个抽象的 ActivityComponent
      • 具体的MainComponent
      • MainActivity和MainFragment怎么注入
    • 另一种写法
  • 总结

提前准备

如果你对Dagger2一点基础都没有,建议你先看看第一篇:Dagger2入门详解

如果想直接看代码,可以 到Github上 Clone一下:源码地址

参考文章

Dependency Injection with Dagger 2

史上最通俗易懂的Android中使用Dagger入门教程

都是套路——Dagger2没有想象的那么难

环境配置

project: build.gradle

dependencies {//...//dagger2classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'}

app: build.gradle

//...//dagger2
apply plugin: 'com.neenbedankt.android-apt'android {//...
}dependencies {//...//dagger2apt 'com.google.dagger:dagger-compiler:2.7'compile 'com.google.dagger:dagger:2.7'provided 'javax.annotation:jsr250-api:1.0'
}

开始撸代码

首先我们要不落俗套的借用一张图:

来解释一下这样图,通俗的理解,我们知道 Dagger2 中一个很重要的概念就是 Scope 生命周期,这里的 component(容器) 的框框可以看成一个容器,并且每个component 一般都拥有自己的 Scope, module 可以看成生产物品并放入 component中的工厂(虽然它不叫 factory)。并且框框里面的component 可以使用外层 component 中的产品。

结合图:ApplicationComponent 是一个容器,它的 ApplicationModule 负责生产一些产品。里层的 ActivityComponent 在 ApplicationComponent中,并享有 ApplicationComponent中的所有产品,并且自己也有 ActivityModule 可以生产自己的产品。 然后再里面就是 UserComponent,它拥有ApplicationComponent和ActivityComponent中的所有产品,并自己也有 Module。这里的产品,就是我们可以用@Inject 注入的东西

其中有两个生命周期 @Singleton 和 @PerActivity(自定义),@Singleton并不是我们设计模式中的单例模式,而是Dagger2中为了保持一个产品在一个Scope中只有一个对象的标签。@PerActivity 也一样,表示在 @PerActivity 这个生命周期中,只包含一个这样产品的标签。

Dagger2 本来只是一个依赖注入框架,再简单不过了。但是非要搞出这么复杂的结构是为了什么?其实这是根据Android开发的特点来的。Fragment 依赖 Activity 依赖 Application 其实本来就有这样的结构,如果你按照这种思想来理解,会容易很多。

好了,上代码

首先建立最大的 AppComponent

AppComponent.java

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {Context getContext();ToastUtil getToastUtil();
}

注意这里的 再 Component中给出的 ToastUtil 是提供给依赖于 AppComponent 的所有 Component的。这里表示所有依赖它的结构都可以使用 toast 功能。

AppModule.java

@Module
public class AppModule {private Context context;public AppModule(Context context) {this.context = context;}@Singleton@Providespublic Context provideContext() {return this.context;}@Singleton@Providespublic ToastUtil provideToastUtil(Context context) {return new ToastUtil(context);}
}

Module中由 @Provides 修饰的表示我这个Component中能提供的产品(供外界注入)。

ToastUtil.java
很简单

public class ToastUtil {private Context context;public ToastUtil(Context context) {this.context = context;}public void showToast(String message) {Toast.makeText(context, message, Toast.LENGTH_SHORT).show();}
}

自定义 App,并将 AppComponent 初始化:
App.java

public class App extends Application {private AppComponent appComponent;@Overridepublic void onCreate() {super.onCreate();appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();}public AppComponent getAppComponent() {return this.appComponent;}
}

别忘了修改 AndroidManifest.xml 文件 <application android:name=".App"> ...</application>

以上就是最大的生命周期 App,而且内容都是 @Singleton 的。然后我们来写 Acitivyt的。

严格按照图上的来写:

写一个抽象的 ActivityComponent

ActivityComponent.java

@PerActivity
@Component(modules = {ActivityModule.class}, dependencies = {AppComponent.class})
public interface ActivityComponent {Activity getActivity();ToastUtil getToastUtil();
}

ActivityModule.java

@Module
public class ActivityModule {private final Activity activity;public ActivityModule(Activity activity) {this.activity = activity;}@PerActivity@Providespublic Activity provideActivity() {return this.activity;}
}

这里注意两点:
1. ActivityComponent 中 还需要重写一次 ToastUtil getToastUtil();, 上面我们提到,Component中写的是 可提供给依赖自己的Component的东西,并且不能直接继承自 AppComponent。这里只需要提供接口,然后它自己会找到AppComponent中并获得ToastUtil
2. @PerActivity,如果使用了 dependencies,那么依赖的一方的 Scope 不能和 父级 相同,其实@PerActivity的代码和 @Singleton 是一样的,只是需要我们自己重新定义一下而已。

PerActivity.java

@Scope
@Documented
@Retention(RUNTIME)
public @interface PerActivity {}

然后写一个 BaseActivity ,它的主要作用其实就是提供 ActivityComponent,因为继承自它的Activity都需要这个容器。

BaseActivity.java

public class BaseActivity extends AppCompatActivity {private ActivityComponent activityComponent;public ActivityComponent getActivityComponent() {return activityComponent;}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);activityComponent = DaggerActivityComponent.builder().appComponent(((App) getApplication()).getAppComponent()).activityModule(new ActivityModule(this)).build();}
}

上面完成了 AppComponent 和 一个抽象的 BaseActivity,为什么叫它抽象,因为你不会用它来实例化一个Activity吧,它只是为了给所有 具体的Activity模块提供内容。当然,还有一种写法,就是不用BaseActivity这个东西,直接让具体的每个Activity和Application发生关系。至于怎么写,我们放在后面吧!

具体的MainComponent

MainComponent.java

@MainActivityScope
@Component(dependencies = {ActivityComponent.class}, modules = {MainModule.class})
public interface MainComponent {void inject(MainActivity mainActivity);MainFragmentComponent mainFragmentComponent();
}

MainModule.java

@Module
public class MainModule {@Providespublic UserRepository provideUserRepository() {return new UserRepository();}
}

MainFragment.java

@MainActivityScope
@Subcomponent
public interface MainFragmentComponent {void inject(MainFragment mainFragment);
}

这里注意几点:

  1. void inject(MainActivity mainActivity);void inject(MainFragment mainFragment); 因为要和具体的依赖组件发生关联,所以添加了注入接口。
  2. 关于 @Subcomponent 的用法,它的作用和 dependencys 相似,这里表示 FragmentComponent 是一个子组件,那么它的父组件是谁呢? 提供了获取 MainFragmentComponent 的组件,如 MainComponent中的 MainFragmentComponent mainFragmentComponent();,是这样做的关联。
  3. MainModule中提供了 UserRepository,表示要给数据仓库,这里只是模拟数据。

UserRepository .java

public class UserRepository  {public UserRepository() {}public User getUser() {User user = new User();user.name = "yxm";return user;}
}

User.java

public class User {public String name;
}
  1. 自定义的 Scope,和前面方法一样,直接copy @Singleton 注解中的代码
    MainActivityScope.java
@Scope
@Documented
@Retention(RUNTIME)
public @interface MainActivityScope {}

MainActivity和MainFragment怎么注入

MainActivity.java

public class MainActivity extends BaseActivity {private MainComponent mainComponent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mainComponent = DaggerMainComponent.builder().activityComponent(getActivityComponent()).mainModule(new MainModule()).build();mainComponent.inject(this);}public MainComponent getMainComponent() {return this.mainComponent;}
}

还是最流行的MVP模式:

MainFragmentContact.java

public class MainFragmentContact {public interface View {void setUserName(String name);void showToast(String msg);}public static class Presenter {public UserRepository userRepository;@Injectpublic Presenter(UserRepository repository) {this.userRepository = repository;}private View view;public void setView(View view) {this.view = view;}public void toastButtonClick() {String msg = "hello world";view.showToast(msg);}public void userInfoButtonClick() {User userData = this.userRepository.getUser();this.view.setUserName((userData.name));}}
}

然后是Fragment

MainFragment.java

public class MainFragment extends Fragment implements MainFragmentContact.View {@InjectMainFragmentContact.Presenter mainPresenter;@InjectToastUtil toastUtil;private MainFragmentComponent mainFragmentComponent;@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);if (getActivity() instanceof MainActivity) {mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();mainFragmentComponent.inject(this);}mainPresenter.setView(this);}@Nullable@Overridepublic android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {android.view.View view = inflater.inflate(R.layout.fragment_main, container, false);return view;}@Overridepublic void onViewCreated(android.view.View view, Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);Button btnToast = (Button) view.findViewById(R.id.btn_toast);btnToast.setOnClickListener(new android.view.View.OnClickListener() {@Overridepublic void onClick(android.view.View view) {mainPresenter.toastButtonClick();}});Button btnUserData = (Button) view.findViewById(R.id.btn_user_info);btnUserData.setOnClickListener(new android.view.View.OnClickListener() {@Overridepublic void onClick(android.view.View view) {mainPresenter.userInfoButtonClick();}});}@Overridepublic void setUserName(String name) {((TextView) getView().findViewById(R.id.et_user)).setText(name);}@Overridepublic void showToast(String msg) {toastUtil.showToast(msg);}
}

代码有点长,注意几点:

  1. 与MainComponent关联上
if (getActivity() instanceof MainActivity) {mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();mainFragmentComponent.inject(this);
}
  1. 内部类注入时,必须使用 static 的,如上的 MainContact 中的 Presenter

  2. 其他的就是MVP方面的知识了,如果不懂,就自己看看呗。然后还有 layout文件,我不信你不会写,哈哈哈

另一种写法

刚刚我们提到,还可以让Activity直接和Application发生关系,怎么写呢?

  1. ActivityComponent 不依赖 AppComponent,所以也不能再提供ToastUtil

ActivityComponent.java

@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {Activity getActivity();
}
  1. MainComponent直接依赖Appcomponent

MainComponent.java

@MainActivityScope
@Component(dependencies = {AppComponent.class}, modules = {MainModule.class, ActivityModule.class})
public interface MainComponent {void inject(MainActivity mainActivity);MainFragmentComponent mainFragmentComponent();
}
  1. BaseActivity中提供 AppComponent的引用,因为所有要注入AppComponent的Activity都需要这个,所以 写在BaseActivity中,同时 ActivityComponent也不需要了。

AppCompatActivity.java

public class BaseActivity extends AppCompatActivity {public AppComponent getAppComponent() {return ((App) getApplication()).getAppComponent();}
}
  1. 注入的地方,添加 AppComponent,同时添加 ActivityModule和MainModule

BaseActivity.java

public class MainActivity extends BaseActivity {private MainComponent mainComponent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mainComponent = DaggerMainComponent.builder().appComponent(getAppComponent()).activityModule(new ActivityModule(this)).mainModule(new MainModule()).build();mainComponent.inject(this);}public MainComponent getMainComponent() {return this.mainComponent;}
}

好了,其他代码不用动,直接可以运行

总结

如果看完并理解了这篇,你肯定能更加理解Dagger2的设计思想。这种分层的结构,可以让我们的项目结构化更加清晰。不要看着上面的代码很复杂,实现的内容又有限。其实,这个架子搭好了,以后往里面加东西就方便了。

现在就可以在各个层级添加自己想要注入的对象的 provideXXX 方法,然后在Activity或者Fragment中直接注入,十分方便,你可以自己体会体会。

当前我们注入的数据源是实体类,其实完全可以注入一个interface,例如 UserFromNet 和 UserFromLocal 表示来自网络和本地的数据源,通过注解来更换数据源。这也是面相抽象编程的思想。

Dagger2实战(详细)相关推荐

  1. sql replace替换多个字符_牛客网数据库SQL实战详细剖析(4150)

    文章来源:大数据肌肉猿 作者:无精疯 这是一个系列文章,总共61题,分6期,有答案以及解题思路,并附上解题的一个思考过程.具体题目可参考牛客网的SQL实战模块:https://www.nowcoder ...

  2. sql not exists用法_牛客网数据库SQL实战详细剖析(5160)(更新完结)

    文章来源:大数据肌肉猿 作者:无精疯 这是一个系列文章,总共61题,分6期,有答案以及解题思路,并附上解题的一个思考过程. 具体题目可参考牛客网的SQL实战模块: https://www.nowcod ...

  3. 【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计

    [Unity]动作游戏开发实战详细分析-07-连续技与组合技功能设计 基本思路 在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等. 实现解析 ...

  4. 【Unity】动作游戏开发实战详细分析-16-敌人AI设计

    [Unity]动作游戏开发实战详细分析-16-敌人AI设计 基本思想 本文来实现简单的敌人AI,使用协程来开发AI.如果想要使用行为树插件可自行学习使用. 代码实现 敌人的目标信息结构 用于存储所有的 ...

  5. 【Unity】动作游戏开发实战详细分析-06-技能系统设计

    [Unity]动作游戏开发实战详细分析-06-技能系统设计 基本思想 不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehavio ...

  6. 【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现

    [Unity]动作游戏开发实战详细分析-25-角色残影效果的实现 基本思路 Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘 ...

  7. 【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统

    [Unity]动作游戏开发实战详细分析-15-可扩展的战斗系统 系统设计 攻击信息传递 通常情况下,伤害.属性.判定都会被封装到类中,在触发动画事件后将战斗信息发送给受击者. 我们可以结合Unity碰 ...

  8. 【Unity】动作游戏开发实战详细分析-24-流血喷溅程序

    [Unity]动作游戏开发实战详细分析-24-流血喷溅程序 溅落血迹效果 实现思路 利用对象池的代码设计思路,通过随机性来实现随机的溅落血迹效果. 代码 public class DripBloodF ...

  9. MySQL之MHA高可用配置及故障切换(理论加实战详细部署步骤)

    MySQL之MHA高可用配置及故障切换(理论加实战详细部署步骤) 文章目录 一.MHA介绍 (一).什么是MHA (二).MHA 的组成 (三).MHA 的特点 二.搭建 MySQL MHA (一). ...

最新文章

  1. C++const关键字作用
  2. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 路由koa-router
  3. win7如何添加开机启动程序(开机就自动运行打开)
  4. 银行业数据中心性能测试的策略与实践(PPT)
  5. 局域网arpsniffer源码剖析
  6. layui表单mysql_layui表单验证支持select下拉框的一种方法
  7. C#集合类型总结和性能分析
  8. linux系统中怎么设置网络,vmware中linux怎么设置网络
  9. java HTML转PDF实现
  10. 【git】小甲鱼Git教程《极客Python之Git实用教程》笔记二
  11. 污染土壤修复可以采取哪些方式
  12. mos管 rl_三极管与MOS管工作状态图解分享
  13. java代理模式学习笔记
  14. vue 使用three.js 实现3D渲染
  15. 2023年全国最新二级建造师精选真题及答案61
  16. 阿里生鲜布局重要抓手!它如何解决全球生鲜商家痛点?
  17. 关于《十天学会AVR单片机》的教程头文件AVR_PQ1A.h
  18. ESP8266进阶篇
  19. Dweb3.0的核心基础设施?NA(Nirvana)Chain加速开凿链上域名流量通道
  20. [猫咪医学大百科]速读笔记

热门文章

  1. 笔记:图解系统(小林coding)
  2. oracle16c,Oracle兵器谱上古神器之-KFED
  3. Python OpenCV 图片模糊操作 blur 与 medianBlur
  4. 智能家居现状是什么,主要面临哪些挑战
  5. 国家授时中心 网络服务器时间,中国国家授时中心的时间服务器IP地址及时间同步方法...
  6. jshell(jshell打不了汉字)
  7. MFC字体磅数与像素CImage
  8. vmlinuz和initrd
  9. 网上找到的有效的关闭UAC的方法。
  10. 永中科技破产清算的疑问(二)