这是一篇Dagger2代码实践的文章,写的不错,拿来记录下,对自己理解帮助很大
环境配置
project: build.gradle
dependencies {
//...
//dagger2
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
app: build.gradle
//...
//dagger2
apply plugin: 'com.neenbedankt.android-apt'
android {
//...
}
dependencies {
//...
//dagger2
apt 'com.google.dagger:dagger-compiler:2.7'
compile 'com.google.dagger:dagger:2.7'
provided 'javax.annotation:jsr250-api:1.0'
}
开始撸代码
首先我们要不落俗套的借用一张图:
[图片上传失败...(image-9c2eb9-1528445377332)]
来解释一下这样图,通俗的理解,我们知道 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
@Provides
public Context provideContext() {
return this.context;
}
@Singleton
@Provides
public 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;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public AppComponent getAppComponent() {
return this.appComponent;
}
}
别忘了修改 AndroidManifest.xml 文件
以上就是最大的生命周期 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
@Provides
public Activity provideActivity() {
return this.activity;
}
}
这里注意两点:
1. ActivityComponent 中 还需要重写一次 ToastUtil getToastUtil();
, 上面我们提到,Component中写的是 可提供给依赖自己的Component的东西,并且不能直接继承自 AppComponent。这里只需要提供接口,然后它自己会找到AppComponent中并获得ToastUtil
- @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;
}
@Override
protected 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 {
@Provides
public 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;
@Override
protected 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;
@Inject
public 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 {
@Inject
MainFragmentContact.Presenter mainPresenter;
@Inject
ToastUtil toastUtil;
private MainFragmentComponent mainFragmentComponent;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() instanceof MainActivity) {
mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();
mainFragmentComponent.inject(this);
}
mainPresenter.setView(this);
}
@Nullable
@Override
public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
android.view.View view = inflater.inflate(R.layout.fragment_main, container, false);
return view;
}
@Override
public 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() {
@Override
public 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() {
@Override
public void onClick(android.view.View view) {
mainPresenter.userInfoButtonClick();
}
});
}
@Override
public void setUserName(String name) {
((TextView) getView().findViewById(R.id.et_user)).setText(name);
}
@Override
public 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;
@Override
protected 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 表示来自网络和本地的数据源,通过注解来更换数据源。这也是面相抽象编程的思想。