依赖注入(DI) 有些人说Android使用依赖注入是因为很多J2EE的人带来的异域思想, 满天飞的注解让人莫不找头脑, 使简单的行为变得复杂, 表面简化, 实则复杂.
但是在使用其一段时间后, 确实还是挺不错的. 正如其思想之精髓, 让你只关注结果 ,而忽略制作过程 , 呵呵, 跟周星驰他老母恰巧相反.
那么下面就讲讲Android开发中常常的用的一些DI框架, 来简化亲们的开发流程吧.
Butterknife Butterknite是一个专注于简化View相关使用的DI框架, 而且其不像Spring一样使用反射, 而通过预编译来做到注入效果, 大大降低了消耗, 绿色环保, 下面就来讲讲他的使用, 其实很简单.
配置: 由于目前基本都是用Android Studio开发, 这里添加gradle的依赖就行了
1 compile 'com.jakewharton:butterknife:6.0.0'
使用方法:
初始化注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this ); } ``` 2. 指定控件的id, 这里定义了 `TextView` `EditText` `Button` 用于测试 3. 使用`Butterknife`注解简化代码 ```Java @InjectView(R.id.search_src_text) TextView textView; @InjectView(R.id.edit_query) EditText editText; @InjectView(R.id.search) Button button; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this ); textView.setText("Hehe" ); } @OnClick(R.id.search) void setEditText () { editText.setText("Haha" ); }
这里可以看出, 以往通过findViewById以及setOnClickListener都得到了简化, 我们冲上去 使用就行了
类似的还有其他的注解, 如@OnLongClick @OnItemClick @OnItemSelected, 用法类似
当layout复用的时候, 有些不一定存在的View可以通过@Optional来标识, 否则会爆炸
详细使用可参照 http://jakewharton.github.io/butterknife/
#Dagger 说简单一些, 使用Dagger注入依旧绿色环保, 省去了日常所关注的构造过程, 让所有指定在Moudle里面的类型, 想用即用.
Dagger的工作原理
配置:
1 2 compile 'com.squareup.dagger:dagger:1.2.2' compile 'com.squareup.dagger:dagger-compiler:1.2.2'
使用方法 1.定义Moudle (告诉Dagger其如何构造) 我们首先先定义一个Android服务相关的Moudle, 简化我们对LocationManager的使用
AndroidModule 1 2 3 4 5 6 7 8 9 @Module(library = true, complete = false) public class AndroidModule { @Provides @Singleton LocationManager provideLocationManager (Application application) { return (LocationManager) application.getSystemService(LOCATION_SERVICE); } }
这里的library = true的意思是可能不会被使用, 这只是一个lib, 默认为false, 如没有被使用会抛错complete = false的意思是这是一个不完整的moudle, 为何这么说, 可以看出provideLocationManager的参数没有响应提供值的Providers呢, 为什么呢, 因为这个Moudle接下来要被另外一个Moudle引用, 所以application这个参数我们将在下一个Moudle里提供.@Provides是必须要有的注解, 告诉Dagger这个方法就是提供LocationManager的方法.@Singleton是标注这是一个单例, 保证了线程安全.
DemoMoudle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Module( //声明将要使用注入对象的类 injects = DemoActivity.class, includes = AndroidModule.class, library = true ) public class DemoModule { private Context application; public DemoModule (Context application) { this .application = application; } @Provides @Singleton Context provideApplicationContext () { return application; } @Provides @Named("hehe") DaggerYa provideDaggerYa1 (Context context) { return new DaggerTa (context, "hehe" ); } @Provides @Named("haha") DaggerYa provideDaggerYa2 (Context context) { return new DaggerTa (context, "haha" ); } @Provides DaggerLa providerDaggerLa (Context context, @Named("hehe") DaggerYa daggerYa) { return new DaggerLa (context, daggerYa); } }
这个Moudle比较长, 首先使用了include来包含我们之前定义的Moudle. 而且也可以发现,这个Moudle提供了Context的Provides
injects比较重要, 这里指定的class就是那些需要使用注入对象的类
有些同学可能会有同样类型的对象注入的问题, 问题的解决办法就是使用@Named来标识其使用的是哪一个@Provides.
2.将Moudle提供给容器ObjectGraph(把之前定义的各个对象的构造方法交给Dagger来维护, 它将会把其注入到程序中去) 由于Application 一个Android程序只有一个, 可以通过getApplication获得,那么最佳存放容器与注入的地方就是这里了,请看代码
DemoApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class DemoApplication extends Application { private ObjectGraph graph; @Override public void onCreate () { super .onCreate(); graph = ObjectGraph.create(getModules().toArray()); } protected List<DemoModule> getModules () { return Arrays.asList(new DemoModule (this )); } public void inject (Object object) { graph.inject(object); } }
3.使用注入对象 劳烦了半天, 终于可以尝尝甜头了, 下面看看使用的办法, 请看代码
DemoActivity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class DemoActivity extends Activity { @Inject LocationManager locationManager; @Inject @Named("hehe") DaggerYa daggerYa; @Inject DaggerLa daggerLa; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); ((DemoApplication) getApplication()).inject(this ); } }
使用注入对象的类, 定义的Moudle里面必须要有这个类的声明(injects = xxx.class), 并且需要调用容器的inject方法(如加粗部分)
下面就可以通过@Inject轻松使用无需再构造 的对象了, 以后只需要添加injects, 并且调用inject方法, 就可以在其他类继续无构造 使用提供过Provides的对象啦
PS: 当然把所有注入对象的构造声明全部放在我们的DemoModule里面也显得有些繁琐, 实际上Dagger也支持标准的Java注入格式. 我们可以直接在自己声明的需要注入 的类上面添加注解 , 来让Dagger找到它的构造函数. 如:
1 2 3 4 5 6 7 8 9 10 11 @Singleton class DaggerHehe { @Inject DaggerHehe(Context context) { ... } @Inject DaggerHehe(DaggerTa ta) { ... } }
在预编译的时候, Dagger就会通过@Inject找到DaggerHehe的构造, 当然DaggerHehe的参数也都需要注入哦. @Singleton同样的将该类声明为一个单例了, 并且还少了之前的@Provider注解, 是不是显得很精简. 赶快去试试吧!
详细可参考: http://square.github.io/dagger/