搭建自己的框架之3:项目中引入Dagger2&Dagger.android
2018-01-18 21:28
751 查看
后台开发Spring很早很早就有依赖注入,Dragger(2) 出现后大型的Android 项目开发依赖管理也美好了。
以前在学习使用Dagger2的时候感觉理解绕,相似的模版代码还很多,哪里需要注入还要写DaggerXXX..inject(this);而且也违背依赖注入的核心原则:一个类不应该知道如何实现依赖注入;它要求注射类型知道其注射器, 即使这是通过接口而不是具体类型完成的。
谷歌大神们又研究出一套专门用于Android的注入方式,拓展Dagger2.android https://google.github.io/dagger//android.html
#为什么不用Dagger2,要等Dagger2.android 出来才用
项目规模小,即使使用Dagger2 也不能显著提高生产力
dagger 难以上手,项目组可能难以推广,或者都是Copy
违背依赖注入核心原则,模版代码多
dagger2.Android 的出现很大程度的解决/缓解了以上问题
#在项目中快速使用
建议小范围或demo 大胆使用,修改添加一些东西慢慢尝试,注释都在代码里面;基本上的根据已有的规则慢慢熟悉就回了,demo 链接 https://github.com/AnyLifeZLB/MVP-Dagger2-Rxjava2,都在代码里详细的注释了。
,,,,
这里有空再写
。。,,
#下面是官方翻译 https://google.github.io/dagger//android.html
Dagger 2相比比其他大多数依赖注入框架的主要优势之一就是其严格生成的实现(无反射)意味着它可以在Android应用程序中使用。然而,在Android开发中使用Dagger还是有一些注意事项。
One of the primary advantages of Dagger 2 over most other dependency injection frameworks is that its strictly generated implementation (no reflection) means that it can be used in Android applications. However, there are still some considerations to be made when using Dagger within Android applications.
While code written for Android is Java source, it is often quite different in terms of style. Typically, such differences exist to accomodate the unique performance considerations of a mobile platform.
但是,通常应用于Android代码的许多模式与应用于其他Java代码的模式相反。甚至很多Effective Java这本书上的建议 对于Android来说都是不合适的。
But many of the patterns commonly applied to code intended for Android are contrary to those applied to other Java code. Even much of the advice in Effective Java is considered inappropriate for Android.
为了达到符合习惯和可移植代码的目标,Dagger依靠ProGuard来后处理编译的字节码。这使得Dagger能够在服务器和Android开发生成的源代码看起来和感觉都是非常的相近而又自然,同时使用不同的工具链来产生在两个环境中都能有效执行的字节码。此外,Dagger有一个明确的目标,确保它生成的Java源代码与ProGuard优化保持一致。
In order to achieve the goals of both idiomatic and portable code, Dagger relies on ProGuard to post-process the compiled bytecode. This allows Dagger to emit source that looks and feels natural on both the server and Android, while using the different toolchains to produce bytecode that executes efficiently in both environements. Moreover, Dagger has an explicit goal to ensure that the Java source that it generates is consistently compatible with ProGuard optimizations.
当然,并不是所有的问题都可以用这种方式来解决,但它是提供Android特定兼容性的主要机制。
Of course, not all issues can be addressed in that manner, but it is the primary mechanism by which Android-specific compatbility will be provided.
Dagger assumes that users on Android will use ProGuard.
Watch this space for ProGuard settings that are relevant to applications using Dagger.
让Dagger2飞起来的东西-
在Android开发中使用Dagger的主要困难是之一是许多Android框架类是由操作系统本身进行实例化的,像 Activity和Fragment,但是如果Dagger可以创建所有的注入对象那就再好不过了。悲剧的是您必须在生命周期方法中执行成员注入。这意味着许多类最终看起来像这个鬼样子:
One of the central difficulties of writing an Android application using Dagger is that many Android framework classes are instantiated by the OS itself, like
这样就会有些问题
This has a few problems:
1.即使Dagger使我们的代码耦合性更低,但是如果要面临重构,我们仍然不得不去面对每个Activity中这样数行需要我们「复制」+「粘贴」的代码,这会给我们的重构带来一定的难度(试想一下,如果我们的应用有数十个乃至上百个这样的Activity或者Fragment容器,我们的重构计划,首先就要面对这样数百行的代码)。
并且随着新的开发人员加入(他们也许并不知道这些代码的意义,但是他们会复制粘贴),越来越少的人知道这些代码都干了些什么。
2.更重要的是,它要求注射类型(FrombulationActivity)知道其注射器。 即使这是通过接口而不是具体类型完成的,它打破了依赖注入的核心原则:一个类不应该知道如何实现依赖注入。
Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does.(项目组中太少人去研究Dagger)
More fundamentally, it requires the type requesting injection (
这打破了依赖注入的核心原则:一个类不应该知道如何依赖注入,最好在需要的地方@Inject就注入完成了✅
The classes in
Injecting
Install
Start off by writing a
After defining the subcomponent, add it to your component hierarchy by defining a module that binds the subcomponent builder and adding it to the component that injects your
Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use
Next, make your
Finally, in your
Congratulations!
Injecting
Injecting a
Instead of injecting in
Unlike the modules defined for
The following types are also included:
Note:
It is crucial to call
Scoping
以前在学习使用Dagger2的时候感觉理解绕,相似的模版代码还很多,哪里需要注入还要写DaggerXXX..inject(this);而且也违背依赖注入的核心原则:一个类不应该知道如何实现依赖注入;它要求注射类型知道其注射器, 即使这是通过接口而不是具体类型完成的。
谷歌大神们又研究出一套专门用于Android的注入方式,拓展Dagger2.android https://google.github.io/dagger//android.html
开始使用
首先在Gradle 中引入依赖,目前dagger-android 还是Beta版本,建议及时更新最新compile 'com.google.dagger:dagger-android:2.13' compile 'com.google.dagger:dagger-android-support:2.13' annotationProcessor 'com.google.dagger:dagger-compiler:2.13' annotationProcessor 'com.google.dagger:dagger-android-processor:2.13'
#为什么不用Dagger2,要等Dagger2.android 出来才用
项目规模小,即使使用Dagger2 也不能显著提高生产力
dagger 难以上手,项目组可能难以推广,或者都是Copy
违背依赖注入核心原则,模版代码多
dagger2.Android 的出现很大程度的解决/缓解了以上问题
#在项目中快速使用
建议小范围或demo 大胆使用,修改添加一些东西慢慢尝试,注释都在代码里面;基本上的根据已有的规则慢慢熟悉就回了,demo 链接 https://github.com/AnyLifeZLB/MVP-Dagger2-Rxjava2,都在代码里详细的注释了。
,,,,
这里有空再写
。。,,
#下面是官方翻译 https://google.github.io/dagger//android.html
Dagger 2相比比其他大多数依赖注入框架的主要优势之一就是其严格生成的实现(无反射)意味着它可以在Android应用程序中使用。然而,在Android开发中使用Dagger还是有一些注意事项。
One of the primary advantages of Dagger 2 over most other dependency injection frameworks is that its strictly generated implementation (no reflection) means that it can be used in Android applications. However, there are still some considerations to be made when using Dagger within Android applications.
哲学问题 Philosophy
虽然为编写Android 程序也是使用的Java,但在风格方面通常是完全不同的。通常情况下,这种差异是为了适应移动平台独特的 性能考虑。While code written for Android is Java source, it is often quite different in terms of style. Typically, such differences exist to accomodate the unique performance considerations of a mobile platform.
但是,通常应用于Android代码的许多模式与应用于其他Java代码的模式相反。甚至很多Effective Java这本书上的建议 对于Android来说都是不合适的。
But many of the patterns commonly applied to code intended for Android are contrary to those applied to other Java code. Even much of the advice in Effective Java is considered inappropriate for Android.
为了达到符合习惯和可移植代码的目标,Dagger依靠ProGuard来后处理编译的字节码。这使得Dagger能够在服务器和Android开发生成的源代码看起来和感觉都是非常的相近而又自然,同时使用不同的工具链来产生在两个环境中都能有效执行的字节码。此外,Dagger有一个明确的目标,确保它生成的Java源代码与ProGuard优化保持一致。
In order to achieve the goals of both idiomatic and portable code, Dagger relies on ProGuard to post-process the compiled bytecode. This allows Dagger to emit source that looks and feels natural on both the server and Android, while using the different toolchains to produce bytecode that executes efficiently in both environements. Moreover, Dagger has an explicit goal to ensure that the Java source that it generates is consistently compatible with ProGuard optimizations.
当然,并不是所有的问题都可以用这种方式来解决,但它是提供Android特定兼容性的主要机制。
Of course, not all issues can be addressed in that manner, but it is the primary mechanism by which Android-specific compatbility will be provided.
tl;dr
所以在Android开发中使用Dagger最好你也使用ProGuardDagger assumes that users on Android will use ProGuard.
推荐的ProGuard设置 Recommended ProGuard Settings
注意在ProGuard设置有对应的考虑你使用了Dagger。Watch this space for ProGuard settings that are relevant to applications using Dagger.
让Dagger2飞起来的东西- dagger.android
在Android开发中使用Dagger的主要困难是之一是许多Android框架类是由操作系统本身进行实例化的,像 Activity和Fragment,但是如果Dagger可以创建所有的注入对象那就再好不过了。悲剧的是您必须在生命周期方法中执行成员注入。这意味着许多类最终看起来像这个鬼样子:One of the central difficulties of writing an Android application using Dagger is that many Android framework classes are instantiated by the OS itself, like
Activityand
Fragment, but Dagger works best if it can create all the injected objects. Instead, you have to perform members injection in a lifecycle method. This means many classes end up looking like:
public class FrombulationActivity extends Activity { @Inject Frombulator frombulator; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // DO THIS FIRST. Otherwise frombulator might be null! //悲剧的是您必须在生命周期方法中执行成员注入 ((SomeApplicationBaseType) getContext().getApplicationContext()) .getApplicationComponent() .newActivityComponentBuilder() .activity(this) .build() .inject(this); // ... now you can write the exciting code } }
这样就会有些问题
This has a few problems:
1.即使Dagger使我们的代码耦合性更低,但是如果要面临重构,我们仍然不得不去面对每个Activity中这样数行需要我们「复制」+「粘贴」的代码,这会给我们的重构带来一定的难度(试想一下,如果我们的应用有数十个乃至上百个这样的Activity或者Fragment容器,我们的重构计划,首先就要面对这样数百行的代码)。
并且随着新的开发人员加入(他们也许并不知道这些代码的意义,但是他们会复制粘贴),越来越少的人知道这些代码都干了些什么。
2.更重要的是,它要求注射类型(FrombulationActivity)知道其注射器。 即使这是通过接口而不是具体类型完成的,它打破了依赖注入的核心原则:一个类不应该知道如何实现依赖注入。
Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does.(项目组中太少人去研究Dagger)
More fundamentally, it requires the type requesting injection (
FrombulationActivity) to know about its injector. Even if this is done through interfaces instead of concrete types, it breaks a core principle of dependency injection: a class shouldn’t know anything about how it is injected.
这打破了依赖注入的核心原则:一个类不应该知道如何依赖注入,最好在需要的地方@Inject就注入完成了✅
The classes in
dagger.androidoffer one approach to simplify this pattern.
Injecting Activity
objects
Install AndroidInjectionModulein your application component to ensure that all bindings necessary for these base types are available.
Start off by writing a
@Subcomponentthat implements
AndroidInjector<YourActivity>, with a
@Subcomponent.Builderthat extends
AndroidInjector.Builder<YourActivity>:
@Subcomponent(modules = ...) public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> { @Subcomponent.Builder public abstract class Builder extends AndroidInjector.Builder<YourActivity> {} }
After defining the subcomponent, add it to your component hierarchy by defining a module that binds the subcomponent builder and adding it to the component that injects your
Application:
@Module(subcomponents = YourActivitySubcomponent.class) abstract class YourActivityModule { @Binds @IntoMap @ActivityKey(YourActivity.class) abstract AndroidInjector.Factory<? extends Activity> bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder); } @Component(modules = {..., YourActivityModule.class}) interface YourApplicationComponent {}
Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use
@ContributesAndroidInjectorto generate them for you. Instead of steps 2 and 3, add an
abstractmodule method that returns your activity, annotate it with
@ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well.
@ActivityScope @ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ }) abstract YourActivity contributeYourActivityInjector();
Next, make your
Applicationimplement
HasActivityInjectorand
@Injecta
DispatchingAndroidInjector<Activity>to return from the
activityInjector()method:
public class YourApplication extends Application implements HasActivityInjector { @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector; @Override public void onCreate() { super.onCreate(); DaggerYourApplicationComponent.create() .inject(this); } @Override public AndroidInjector<Activity> activityInjector() { return dispatchingActivityInjector; } }
Finally, in your
Activity.onCreate()method, call
AndroidInjection.inject(this)before calling
super.onCreate();:
public class YourActivity extends Activity { public void onCreate(Bundle savedInstanceState) { AndroidInjection.inject(this); super.onCreate(savedInstanceState); } }
Congratulations!
How did that work?
AndroidInjection.inject()gets a
DispatchingAndroidInjector<Activity>from the
Applicationand passes your activity to
inject(Activity). The
DispatchingAndroidInjectorlooks up the
AndroidInjector.Factoryfor your activity’s class (which is
YourActivitySubcomponent.Builder), creates the
AndroidInjector(which is
YourActivitySubcomponent), and passes your activity to
inject(YourActivity).
Injecting Fragment
objects
Injecting a Fragmentis just as simple as injecting an
Activity. Define your subcomponent in the same way, replacing
Activitytype parameters with
Fragment,
@ActivityKeywith
@FragmentKey, and
HasActivityInjectorwith
HasFragmentInjector.
Instead of injecting in
onCreate()as is done for
Activitytypes, inject
Fragments to in
onAttach().
Unlike the modules defined for
Activitys, you have a choice of where to install modules for
Fragments. You can make your
Fragmentcomponent a subcomponent of another
Fragmentcomponent, an
Activitycomponent, or the
Applicationcomponent — it all depends on which other bindings your
Fragmentrequires. After deciding on the component location, make the corresponding type implement
HasFragmentInjector. For example, if your
Fragmentneeds bindings from
YourActivitySubcomponent, your code will look something like this:
public class YourActivity extends Activity implements HasFragmentInjector { @Inject DispatchingAndroidInjector<Fragment> fragmentInjector; @Override public void onCreate(Bundle savedInstanceState) { AndroidInjection.inject(this); super.onCreate(savedInstanceState); // ... } @Override public AndroidInjector<Fragment> fragmentInjector() { return fragmentInjector; } } public class YourFragment extends Fragment { @Inject SomeDependency someDep; @Override public void onAttach(Activity activity) { AndroidInjection.inject(this); super.onAttach(activity); // ... } } @Subcomponent(modules = ...) public interface YourFragmentSubcomponent extends AndroidInjector<YourFragment> { @Subcomponent.Builder public abstract class Builder extends AndroidInjector.Builder<YourFragment> {} } @Module(subcomponents = YourFragmentSubcomponent.class) abstract class YourFragmentModule { @Binds @IntoMap @FragmentKey(YourFragment.class) abstract AndroidInjector.Factory<? extends Fragment> bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder); } @Subcomponent(modules = { YourFragmentModule.class, ... } public interface YourActivityOrYourApplicationComponent { ... }
Base Framework Types
BecauseDispatchingAndroidInjectorlooks up the appropriate
AndroidInjector.Factoryby the class at runtime, a base class can implement
HasActivityInjector/
HasFragmentInjector/etc as well as call
AndroidInjection.inject(). All each subclass needs to do is bind a corresponding
@Subcomponent. Dagger provides a few base types that do this, such as
DaggerActivityand
DaggerFragment, if you don’t have a complicated class hierarchy. Dagger also provides a
DaggerApplicationfor the same purpose — all you need to do is to extend it and override the
applicationInjector()method to return the component that should inject the
Application.
The following types are also included:
DaggerServiceand
DaggerIntentService
DaggerBroadcastReceiver
DaggerContentProvider
Note:
DaggerBroadcastReceivershould only be used when the
BroadcastReceiveris registered in the
AndroidManifest.xml. When the
BroadcastReceiveris created in your own code, prefer constructor injection instead.
Support libraries
For users of the Android support library, parallel types exist in thedagger.android.supportpackage. Note that while support
Fragmentusers have to bind
AndroidInjector.Factory<? extends android.support.v4.app.Fragment>, AppCompat users should continue to implement
AndroidInjector.Factory<? extends Activity>and not
<? extends AppCompatActivity>(or
FragmentActivity).
How do I get it?
Add the following to your build.gradle:dependencies { compile 'com.google.dagger:dagger-android:2.x' compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries annotationProcessor 'com.google.dagger:dagger-android-processor:2.x' }
When to inject
Constructor injection is preferred whenever possible becausejavacwill ensure that no field is referenced before it has been set, which helps avoid
NullPointerExceptions. When members injection is required (as discussed above), prefer to inject as early as possible. For this reason,
DaggerActivitycalls
AndroidInjection.inject()immediately in
onCreate(), before calling
super.onCreate(), and
DaggerFragmentdoes the same in
onAttach(), which also prevents inconsistencies if the
Fragmentis reattached.
It is crucial to call
AndroidInjection.inject()before
super.onCreate()in an
Activity, since the call to
superattaches
Fragments from the previous activity instance during configuration change, which in turn injects the
Fragments. In order for the
Fragmentinjection to succeed, the
Activitymust already be injected. For users of ErrorProne, it is a compiler error to call
AndroidInjection.inject()after
super.onCreate().
FAQ
Scoping AndroidInjector.Factory
AndroidInjector.Factoryis intended to be a stateless interface so that implementors don’t have to worry about managing state related to the object which will be injected. When
DispatchingAndroidInjectorrequests a
AndroidInjector.Factory, it does so through a
Providerso that it doesn’t explicitly retain any instances of the factory. Because the
AndroidInjector.Builderimplementation that is generated by Dagger does retain an instance of the
Activity/
Fragment/etc that is being injected, it is a compile-time error to apply a scope to the methods which provide them. If you are positive that your
AndroidInjector.Factorydoes not retain an instance to the injected object, you may suppress this error by applying
@SuppressWarnings("dagger.android.ScopedInjectoryFactory")to your module method.
相关文章推荐
- 自己搭建Android项目框架必备的框架与第三方应用
- android ui定义自己的dialog(项目框架搭建时就写好,之后事半功倍)
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(2)--之MVP引入
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(1)--之Dagger2引入
- Android中搭建自己的项目网络框架
- android ui定义自己的dialog(项目框架搭建时就写好,之后事半功倍)
- Android搭建项目框架
- android studio引入其他项目中的lib module到自己的项目中作为lib
- 自己动手搭建一个移动端React+Redux+Webpack3项目框架
- 尚硅谷Android项目之_硅谷商城项目全套源码解析(二、主框架搭建)
- Android项目框架搭建
- 搭建高质量的Android项目框架系列四
- 手把手教你搭建自己的Java Web(Android)项目(SpringMVC + Mybatis服务端,Html5 Web端, Android客户端实现)
- 从框架到完整项目搭建,实战项目《约个球》(7)- 将项目迁移到android studio,使用gradle搭建android项目(上)
- Android Vuforia项目结构的搭建 SDK的引入
- Android学习之——自己搭建Http框架(2)——框架扩展
- 1、Android项目框架搭建 (分析需求、整理资料)
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(3)--完美实现
- Android项目框架搭建 (所需要的资料)