Dagger2实战(详细)
- 提前准备
- 参考文章
- 环境配置
- 开始撸代码
- 好了上代码
- 首先建立最大的 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);
}
这里注意几点:
void inject(MainActivity mainActivity);
和void inject(MainFragment mainFragment);
因为要和具体的依赖组件发生关联,所以添加了注入接口。- 关于 @Subcomponent 的用法,它的作用和 dependencys 相似,这里表示 FragmentComponent 是一个子组件,那么它的父组件是谁呢? 提供了获取 MainFragmentComponent 的组件,如 MainComponent中的
MainFragmentComponent mainFragmentComponent();
,是这样做的关联。 - 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;
}
- 自定义的 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);}
}
代码有点长,注意几点:
- 与MainComponent关联上
if (getActivity() instanceof MainActivity) {mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();mainFragmentComponent.inject(this);
}
内部类注入时,必须使用 static 的,如上的 MainContact 中的 Presenter
其他的就是MVP方面的知识了,如果不懂,就自己看看呗。然后还有 layout文件,我不信你不会写,哈哈哈
另一种写法
刚刚我们提到,还可以让Activity直接和Application发生关系,怎么写呢?
- ActivityComponent 不依赖 AppComponent,所以也不能再提供ToastUtil
ActivityComponent.java
@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {Activity getActivity();
}
- MainComponent直接依赖Appcomponent
MainComponent.java
@MainActivityScope
@Component(dependencies = {AppComponent.class}, modules = {MainModule.class, ActivityModule.class})
public interface MainComponent {void inject(MainActivity mainActivity);MainFragmentComponent mainFragmentComponent();
}
- BaseActivity中提供 AppComponent的引用,因为所有要注入AppComponent的Activity都需要这个,所以 写在BaseActivity中,同时 ActivityComponent也不需要了。
AppCompatActivity.java
public class BaseActivity extends AppCompatActivity {public AppComponent getAppComponent() {return ((App) getApplication()).getAppComponent();}
}
- 注入的地方,添加 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实战(详细)相关推荐
- sql replace替换多个字符_牛客网数据库SQL实战详细剖析(4150)
文章来源:大数据肌肉猿 作者:无精疯 这是一个系列文章,总共61题,分6期,有答案以及解题思路,并附上解题的一个思考过程.具体题目可参考牛客网的SQL实战模块:https://www.nowcoder ...
- sql not exists用法_牛客网数据库SQL实战详细剖析(5160)(更新完结)
文章来源:大数据肌肉猿 作者:无精疯 这是一个系列文章,总共61题,分6期,有答案以及解题思路,并附上解题的一个思考过程. 具体题目可参考牛客网的SQL实战模块: https://www.nowcod ...
- 【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计
[Unity]动作游戏开发实战详细分析-07-连续技与组合技功能设计 基本思路 在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等. 实现解析 ...
- 【Unity】动作游戏开发实战详细分析-16-敌人AI设计
[Unity]动作游戏开发实战详细分析-16-敌人AI设计 基本思想 本文来实现简单的敌人AI,使用协程来开发AI.如果想要使用行为树插件可自行学习使用. 代码实现 敌人的目标信息结构 用于存储所有的 ...
- 【Unity】动作游戏开发实战详细分析-06-技能系统设计
[Unity]动作游戏开发实战详细分析-06-技能系统设计 基本思想 不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehavio ...
- 【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现
[Unity]动作游戏开发实战详细分析-25-角色残影效果的实现 基本思路 Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘 ...
- 【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统
[Unity]动作游戏开发实战详细分析-15-可扩展的战斗系统 系统设计 攻击信息传递 通常情况下,伤害.属性.判定都会被封装到类中,在触发动画事件后将战斗信息发送给受击者. 我们可以结合Unity碰 ...
- 【Unity】动作游戏开发实战详细分析-24-流血喷溅程序
[Unity]动作游戏开发实战详细分析-24-流血喷溅程序 溅落血迹效果 实现思路 利用对象池的代码设计思路,通过随机性来实现随机的溅落血迹效果. 代码 public class DripBloodF ...
- MySQL之MHA高可用配置及故障切换(理论加实战详细部署步骤)
MySQL之MHA高可用配置及故障切换(理论加实战详细部署步骤) 文章目录 一.MHA介绍 (一).什么是MHA (二).MHA 的组成 (三).MHA 的特点 二.搭建 MySQL MHA (一). ...
最新文章
- C++const关键字作用
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 路由koa-router
- win7如何添加开机启动程序(开机就自动运行打开)
- 银行业数据中心性能测试的策略与实践(PPT)
- 局域网arpsniffer源码剖析
- layui表单mysql_layui表单验证支持select下拉框的一种方法
- C#集合类型总结和性能分析
- linux系统中怎么设置网络,vmware中linux怎么设置网络
- java HTML转PDF实现
- 【git】小甲鱼Git教程《极客Python之Git实用教程》笔记二
- 污染土壤修复可以采取哪些方式
- mos管 rl_三极管与MOS管工作状态图解分享
- java代理模式学习笔记
- vue 使用three.js 实现3D渲染
- 2023年全国最新二级建造师精选真题及答案61
- 阿里生鲜布局重要抓手!它如何解决全球生鲜商家痛点?
- 关于《十天学会AVR单片机》的教程头文件AVR_PQ1A.h
- ESP8266进阶篇
- Dweb3.0的核心基础设施?NA(Nirvana)Chain加速开凿链上域名流量通道
- [猫咪医学大百科]速读笔记