Dagger是什么和我们怎么通过使用它获得收获
来源:互联网 发布:mac pdf怎么签名 编辑:程序博客网 时间:2024/04/29 19:14
原文:https://github.com/konmik/konmik.github.io/wiki/Snorkeling-with-Dagger-2
Dagger是什么
Dagger是为对象实例提供选择的Java库。你可以使用注解@Inject
来标注你的构造函数,替代在构造函数中传递大量参数,并且所有需要的对象被创建和自动分配。
@Inject SharedPreferences pref;
这会通过一个资源代码生成来完成-你注释一些field
,Dagger会创建代码来完成这些field。你可以经常在build
文件夹中检查这些生成的代码。
把Dagger作为Java的扩展。在一般面向对象语言中,对象的创建、存储、传递会存在大量的问题,Dagger提供了一种优雅的解决方式。
历史
Dagger被Square开发,项目地址:square.github.io/dagger
Dagger2是Squared的Dagger的一个分支,现在有Google负责开发并且被用在性能关键项目:此处输入链接的描述
好处:单例
在Android项目中,一个典型的任务就是创建一个单例的SharedPreferences
对象,并且在整个Application中共享。它有可能出现在activity、fragment、自定义View中。典型的解决办法是在Application
中创建一个SharedPreference
,并为它创建一个get方法。然后,你的代码会想这样:
public class MainActivity extends Activity { SharedPreferences pref; Gson gson; ServerAPI api; onCreate(...) { MyApp app = (MyApp)getContext().getApplicationContext(); pref = app.getSharedPreferences(); gson = app.getGson(); api = app.getApi();
有一天你会发现MyApp
被奇怪的东西充斥着,并且它们跟MyApp
没有关联。或者你可能发现你到处使用丑陋的单例模式。现在,想想重构的事。你决定将Gson
从MyApp
移到MyNetwor
k中,你需要重写多少行代码?
使用Dagger的方式:
public class MainActivity extends Activity { @Inject SharedPreferences pref; @Inject Gson gson; @Inject ServerAPI api; onCreate(...) { MainInjector.inject(this);
现在我们不关心到哪里拿到这些东西,这些东西会为我们创建。Gson
可能会被放在MyApp
对象里,也可能在MyNetWork.Parsing.Processor
的任何地方。我们只要关注Gson
!对的,我们需要在Dagger中定义一次如何获得Gson
,但是之后我们会通过@Inject
注解在整个application中共享它。
基础例子
使用@Inject
注入构造函数
我们先创建一个最简单的例子。我们将注入一个用来打印所有SharedPreferences
的对象PreferencesLogger
。
public class PreferencesLogger { @Inject public PreferencesLogger() { } public void log(Context context) { SharedPreferences pref = context.getSharedPreferences("preferences", 0); Log.v(getClass().getSimpleName(), "Logging all preferences:"); for (Map.Entry entry : pref.getAll().entrySet()) Log.v(getClass().getSimpleName(), entry.getKey() + " = " + entry.getValue()); }}
@Inject
对Dagger来说是一个用来实例化PreferencesLogger
的构造器。上面的实现有一点笨但是我们之后会把SharedPreferences
传递给PreferencesLogger
的构造函数,那样看起来会好一些。
Injection
下面是我么如何使用PreferencesLogger
:
public class MainActivity extends Activity { @Inject PreferencesLogger logger; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyApplication.inject(this); logger.log(this); }}
生成代码
你是不是好奇MyApplication.inject
是什么样子的?但是你现在还不能立刻看到。:) 首先,你要看一张图:
当我们使用Dagger2的时候,我们需要记住它的构成。你要知道@Inject
这个构造器,下一个是要知道的是@Component
。Component是用@Component
注解标记的接口,展示了那些我们想注入到Dagger2中的对象。
@Componentpublic interface AppComponent { void inject(MainActivity activity);}
@Componnet
对Dagger2的意义是,它应该为注入MainActivity生成代码。
当我们这些都写了,我们点击Build
按钮让Dagger2生成AppComponent
的实现。
最后:注入代码
public class MyApplication extends Application { private static AppComponent component; @Override public void onCreate() { super.onCreate(); component = DaggerAppComponent.builder().build(); } public static void inject(MainActivity target) { component.inject(target); }}
DaggerAppComponent
是AppComponent
的实现类,我们需要访问AppComponent
的实现代码。IDE看不到这个实现类,不能把它标记成红色,但是之后我会展示如何处理这个问题。
现在我们已经有了一个完整可以正常运行的例子了!:)
对的,Dagger在开始之前还需要做一点事情,(别灰心)思考一下它带来的优势!在我的工程中,平均有50个左右的对象需要通过application去共享。Dagger为我节省重写的代码量是不可思议的。在使用Dagger之前,任何时候都要避免整合代码。现在我整合代码既快又简单,在对象之间没有负面效果或者关联失败的现象。
我希望你一直坚持Dagger,如果那样,我们可以一起学习更多先进的课题。
探索生成的代码
让我们看一下生成的代码,为了缩减这个指南,我已经去掉所有没用的代码。:)
public final class DaggerAppComponent implements AppComponent { private MembersInjector mainActivityMembersInjector; private DaggerAppComponent(Builder builder) {(3) initialize(); } public static Builder builder() {(1) return new Builder(); // Builder instantiation } private void initialize() {(4) this.mainActivityMembersInjector = MainActivity_MembersInjector.create(MembersInjectors.noOp(), PreferencesLogger_Factory.create()); } @Override public void inject(MainActivity activity) {(9) mainActivityMembersInjector.injectMembers(activity); } public static final class Builder { public AppComponent build() {(2) return new DaggerAppComponent(this); // DaggerAppComponent instantiation } }}public final class MainActivity_MembersInjector implements MembersInjector { private final MembersInjector supertypeInjector; private final Provider loggerProvider; public MainActivity_MembersInjector(MembersInjector supertypeInjector, Provider loggerProvider) {(7) this.supertypeInjector = supertypeInjector;(8) this.loggerProvider = loggerProvider; } @Override public void injectMembers(MainActivity instance) {(10) supertypeInjector.injectMembers(instance);(11) instance.logger = loggerProvider.get(); } public static MembersInjector create(MembersInjector supertypeInjector, Provider loggerProvider) {(6) return new MainActivity_MembersInjector(supertypeInjector, loggerProvider); }}public enum PreferencesLogger_Factory implements Factory { INSTANCE; @Override public PreferencesLogger get() {(12) return new PreferencesLogger(); // @Inject annotated constructor call } public static Factory create() {(5) return INSTANCE; }}
没有生成注释,但是已经非常清晰的展示了这个简单的代码的工作方式。我仅仅列出一些代码混淆而缺失的代码。
public interface Factory extends javax.inject.Provider {}public interface Provider { T get();}public interface MembersInjector { void injectMembers(T t);}public static MembersInjector noOp() { return (MembersInjector) NoOpMembersInjector.INSTANCE;}private static enum NoOpMembersInjector implements MembersInjector { INSTANCE; @Override public void injectMembers(Object instance) { }}
你还有一些事情要做:执行下面代码,跟踪执行的步骤。
DaggerAppComponent.builder().build().inject(MainActivity.this);
这里会发生什么?
- 调用静态方法DaggerAppComponent.builder()
来创建一个Builder
实例;
- Builder
创建了一个DaggerAPPComponent
实例;
- DaggerAPPComponent
创建了一个MainActivity_MembersInjector
实例;
- MainActivity_MembersInjector
使用PreferencesLogger_Factory
去实例化PreferencesLogger
,并将其注入到MainActivity
。
好了,到这里,你应该明白了Dagger2的基本工作原理,是时候喝杯茶休息一会,15mins后继续。:D
注入第三方的类
Module的定义
module是程序的一部分,当程序的另一个组成部分Component用到一些功能的时候,它用来提供这些功能。你可以拥有不止一个module,例如你可以创建DatabaseModule
、NetworkModule
、MainActivityModule
等等。component也是同样道理。
让我们围绕共享一个SharedPreferences
创建下一个例子。首先,我们需要告诉Dagger我们如何实例化SharedPreferences
。明显我们不能使用@Inject
来标记构造函数,所以我们要像这样创建一个Module:
@Modulepublic class AppModule { private static final String PREFERENCES_FILE_NAME = "preferences"; private MyApplication app; AppModule(MyApplication app) { this.app = app; } @Singleton @Provides SharedPreferences provideSharedPreferences() { return app.getSharedPreferences(PREFERENCES_FILE_NAME, 0); }}
@Module
对dagger来说,它是用来对象实例化的。 @Provides
意味着他标记的这个方法应该被实例化的对象所调用。 @Singleton
意味着这个对象应该被重新注入其他对象中。
这里有一张小图表说明了Dagger2是如何使用Module的:
改写PreferencesLogger
现在我们不想在PreferencesLogger``中实例化
SharedPreferences,我们可以在
PreferencesLogger的构造函数中声明它,Dagger构造出
SharePreferences`的实例。
public class PreferencesLogger { private SharedPreferences pref; @Inject public PreferencesLogger(SharedPreferences pref) { this.pref = pref; } public void log() { Log.v(getClass().getSimpleName(), "Logging all preferences:"); for (Map.Entry entry : pref.getAll().entrySet()) Log.v(getClass().getSimpleName(), entry.getKey() + " = " + entry.getValue()); }}
我们有两种方式–作为构造函数的参数和注入标记。作为构造器的参数有更多的可控性–如果你通过构造器注入一个对象,你可以在构造函数中正确的使用它。
如果你用@Inject SharedPreferences pref
的方式注入,它会在执行构造函数之后注入SharedPreference
。代码就会这个样子:
public class PreferencesLogger { @Inject SharedPreferences pref; @Inject public PreferencesLogger() { } ...
这两种方法做的是同一样事情。注意第二种方式中没有private
修饰词修饰,因为Dagger需要通过这种方式进行注入。
改写AppComponent
@Singleton@Component(modules = AppModule.class)public interface AppComponent { void inject(MainActivity activity);}
@Singleton
说明了这个component会以一个单例的形式存在。 (modules = AppModule.class)
意味着APPModule会被用于APPComponent的注入。你可以在这里列出多个module,例如:(modules = {AppModule.class, MainActivityModule.class})
如果你想在这个component中注入多个对象,你需要向这样写:
@Singleton@Component(modules = AppModule.class)public interface AppComponent { void inject(MainActivity activity); void inject(MainFragment fragment); void inject(MainToolbarView view);}
改写AppComponent
的实例
component = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
就这样,你现在知道如何在Dagger2中使用第三方类了吧。
奇迹
Component更容易的实例化
如果你尝试使用了Dagger2,你会注意到在程序编译无误的情况下,IDE找不到DaggerAPPComponent并用红色标记成错误。一切都看起来正常和干净,那这个问题该如何避免呢。
一个可行的解决办法–使用android-aptplugin。
在我浏览GitHub的时候,在Mortar我发现了解决这个问题的另外一个有趣办法。我使用buildComponent方法来实例化component并把它复制到我的项目例子中。Dagger2Helper
第二种方法使用反射,但是它没有产生一点明显的性能影响。在最慢的设备上,在程序运行15ms内就可以获得它们。
这就是它们的样子:
public class MyApplication extends Application { private static AppComponent component; @Override public void onCreate() { super.onCreate(); // component = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); component = Dagger2Helper.buildComponent(AppComponent.class, new AppModule(this)); } ...
更容易注入和继承
给一个稍稍修改我们注入方法实现更容易注入的小贴士。将MyApplication.inject(...)
改成MyApplication.getComponent().inject(...)
。你可以提取注入器并这样调用MainInjector.getComponent().inject(...)
。
另一种方式依赖于反射,所以我们啰嗦的描述它。
在Dagger1的旧时代,我们只有一种void inject(Object object)
方法。它不是很明显的很快很方便。例如,我们可以在一个基类中写inject(this)
,它所有的子类不用调用Dagger都可以拿到那些注入。这是非常的方便,所以我决定复制(学习)这样的行为。
这是个例子:
public class BaseTarget { @Inject protected MyApplication app; public BaseTarget() { Log.v(getClass().getSimpleName(), "app before injection: " + app); MyApplication.getComponent().inject(this); Log.v(getClass().getSimpleName(), "app after injection: " + app); }}
为了满足这种依赖性,我会在APPModule
中加入下面这些代码:
@ProvidesMyApplication provideApp() { return app;}
子类就可以不通过直接调用injet(...)
拿到注入:
public class RealTarget extends BaseTarget { @Inject SharedPreferences pref; public RealTarget() { } public void check() { Log.v(getClass().getSimpleName(), "Base injection app: " + app); Log.v(getClass().getSimpleName(), "Real injection pref: " + pref); }}
调用new RealTarget().check()
会有下面输出:
app before injection: nullapp after injection: info.android15.dagger2example.MyApplication@419412e0Base injection app: info.android15.dagger2example.MyApplication@419412e0Real injection pref: null
就像你看到的,还没有满足子类依赖SharedPreferences
,因为典型的Dagger2使用模式是你应该在RealTarget
中调用inject(...)
。如果你喜欢创建像BaseActivity
、BaseCustomView
、BaseAdapter
等等,这样的方式就不满足。
现在我会用inject
方法重写Dagger1实现更容易的注入。父类:Dagger2Helper:
public class MyApplication extends Application { ... public static void inject(Object target) { Dagger2Helper.inject(AppComponent.class, component, target); }}public class BaseTarget { @Inject protected MyApplication app; public BaseTarget() { Log.v(getClass().getSimpleName(), "app before injection: " + app); // MyApplication.getComponent().inject(this); MyApplication.inject(this); Log.v(getClass().getSimpleName(), "app after injection: " + app); }}
输出:
app before injection: nullapp after injection: info.android15.dagger2example.MyApplication@419430e0Base injection app: info.android15.dagger2example.MyApplication@419430e0Real injection pref: android.app.SharedPreferencesImpl@419630d8
- Dagger是什么和我们怎么通过使用它获得收获
- SQL注入是什么?我们如何去玩转它
- Gamma空间是什么,为什么我们需要它
- Dagger使用
- 通过UIView 获得它的控制器方法
- 收获是什么?
- bower 是什么,怎么使用
- [译] Web Components 是什么?它为什么对我们这么重要?
- SDL是什么,能干什么,为什么我们要学习它?
- HDR到底是什么?今天我们来把它说个透
- GreenDao_1.怎么开始使用它
- App.xaml用途是什么?怎么传递参数和使用?
- App.xaml用途是什么?怎么传递参数和使用?
- 对于我们应该怎么去增加它的收录
- 在Eclipse中搭建Dagger和Dagger2使用环境
- Android依赖注入Dagger的使用和源码解析
- 图论系列 --- 1. 图是什么玩意, 怎么表示它?
- dagger的使用
- struts 迭代器iterator四种使用方式 S标签
- oracle 修改用户名
- HDU 3555 Bomb (数位DP)
- C语言实现单链表——之基本操作1
- Centos修改语言为英文
- Dagger是什么和我们怎么通过使用它获得收获
- Vysor使用——用电脑操作手机 无需Root
- Tuning Windows Server 2008 for PHP
- 如何给20151230版caffe添加新的层 (一)
- NTFS文件系统详解(一)之硬盘基本信息
- Http协议和Https协议的区别
- 获取Android SHA1与Package
- 动态修改 drawableTop
- 链表的简单创建——C程序设计