依赖注入(DependencyInjection):在类A中要用到一个B的对象(依赖),需要通过新建B的实例或其他一些主动的方式来获取对象,然后才能调用。而通过外部的方式自动将B的对象分配给A(注入),实现相对被动的获取对象,这个过程称为依赖注入。
依赖注入的一些需要理解的概念,具体可以可再在维基百科上找到更详细的解释。
控制反转:对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
依赖注入:被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中。
依赖注入有如下实现方式:
· 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
· 基于 set方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
· 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
· 基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。
依赖查找:主动索取响应名称的对象,获得依赖对象的时间也可以在代码中自由控制。
依赖反转原则:
1. 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
2. 抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。
应用依赖反转原则同样被认为是应用了适配器模式,例如:高层的类定义了它自己的适配器接口(高层类所依赖的抽象接口)。被适配的对象同样依赖于适配器接口的抽象(这是当然的,因为它实现了这个接口),同时它的实现则可以使用它自身所在低层模块的代码。通过这种方式,高层组件则不依赖于低层组件,因为它(高层组件)仅间接的通过调用配置器接口多态方法的方式使用了低层组件通过适配器接口,在这些多态方法则是由被适配对象以及它的低层模块所实现的。
Android依赖注入目的:
· 1. 减少样板代码
· 2. 提高重用性,测试性,可维护性.
Android依赖注入库
1. ButterKnife
@Bind(R.id.title) TextView title;
@bind和viewId相结合能够自动联系到layout的view(使用场景activity和fragment、viewholder等需要与view结合的地方都可以使用)
在使用@bind结合view之后,需要调用ButterKnife.bind(this)或者ButterKnife.bind(this,view)方式真正实现依赖注入,则可以代替findViewByID
特例:在Fragment生命周期中,onDestoryView也需要Butterknife.unbind(this)
2. Dagger
依赖注入框架,Dagger两种主要方法:
第一,@Inject写到对象类的构造函数上面,要用的时候直接注入对象,避免new对象,造成强耦合。且可以加上注释@Singleton形成单例。
第二,以@module和@provides为基础,可以任意注入对象,且可以带参操作。Module里面若是要有其他Module,要在includes里写好,@Module除了include,还有injects参数指定注入位置,library指定是否需要外部库,complete是否是完整module(含有外部module依赖则不完整)。
最后在注入目的地,通过ObjectGraph(对象图表)形成依赖关系,具体操作方式,ObjectGraph对象,并调用方法create(Module).inject(注入对象);则可以直接用@inject注入目标对象类里。具体流程如图所示。
(此图摘自http://talentprince.github.io/blog/2015/01/04/android-yu-inject-yi-lai-zhu-ru-de-bu-jie-zhi-yuan/)
3. Dagger和butterknife区别
Buffer knife目的为注入到view,所以能够在非activity里面注入,也能注入到inflate的views里面
Dagger能够注入到任何你想要的对象,只要其在module类中。或者它是构造器。但是缺少对方法和字段的注入支持。
Buffer knife只是避免样板代码,findViewById,仅此而已,所以不能算是一个真正的注入。只是一个view的代言。
对于Dagger我们可以把它当做应用中的一个模块, 负责为其它模块提供实例并且注入依赖关系。那是它的基本职责。模块的创建位于我们应用中的一个点上,这样我们可以拥有完全的控制权。Dagger Leiva说,特别适合用在低端设备上,因为它没有采取反射而使用了预编译技术,因为基于反射的DI非常占用资源和耗时。Dagger或许不是最理想的依赖注入框架,但Leiva认为,它是最高效的。
4. Buffer knife和Dragger综合实战
建立一个list列表,通过Dragger每次点击按钮依赖注入新对象,实现逐渐填充list列表。
布局文件 activity_main.xml
package com.hao.injectdemo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends ActionBarActivity implements View.OnClickListener {
@Bind(R.id.button)//Buffer knife注入
Button button;
@Bind(R.id.list_item)//Buffer knife注入
ListView mListView;
@Inject
ListProduct listProduct;//Dagger注入
static List
ListProduct构造新的Map填充list(运用Dagger注入构造对象)
package com.hao.injectdemo;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
/**
* Created by hao on 7/4/2015.
*/
public class ListProduct {
public ListProduct() {
}
public Map putMap() {
Map map = new HashMap();
map.put("text", String.valueOf(MainActivity.count++));
map.put("text2", String.valueOf(MainActivity.count++));
MainActivity.mlist.add(map);
return map;
}
}
package com.hao.injectdemo;
import dagger.Module;
import dagger.Provides;
/**
* Created by hao on 7/2/2015.
*/
@Module(injects = MainActivity.class)//module和provides配合
public class AppModule {
@Provides
ListProduct getWorkAdapter() {
return new ListProduct();
}
}
package com.hao.injectdemo;
import android.app.Application;
import dagger.ObjectGraph;
/**
* Created by hao on 7/2/2015.
*/
public class App extends Application {
private ObjectGraph objectGraph;
public void inject(T target) {
getObjectGraph().inject(target);
}
private ObjectGraph getObjectGraph() {
if (objectGraph == null) {
objectGraph = ObjectGraph.create(AppModule.class);
}
return objectGraph;
}
@Override
public void onCreate() {
}
}
package com.hao.injectdemo;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
import java.util.Map;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
* Created by hao on 7/4/2015.
*/
public class WorkAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater = null;
List> mList;
Context mContext;
public WorkAdapter(Context mContext, List> list) {
this.mLayoutInflater = LayoutInflater.from(mContext);
this.mList = list;
this.mContext = mContext;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listadapter, null);
viewHolder = new ViewHolder(convertView, mContext);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.text1.setText(mList.get(position).get("text"));
viewHolder.text2.setText(mList.get(position).get("text2"));
return convertView;
}
static class ViewHolder {
@Bind(R.id.text)//针对其他view的Buffer knife注入
TextView text1;
@Bind(R.id.text_2)//针对其他view的Buffer knife注入
TextView text2;
public ViewHolder(View view, Context mContext) {
ButterKnife.bind(this, view);//Buffer knife建立联系
}
}
}
最终结果
参考链接
https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC
https://en.wikipedia.org/wiki/Dependency_injection
https://zh.wikipedia.org/zh/%E4%BE%9D%E8%B5%96%E5%8F%8D%E8%BD%AC%E5%8E%9F%E5%88%99
Dagger介绍:
http://www.infoq.com/cn/news/2012/10/dagger
http://square.github.io/dagger/
http://fanxu.me/post/2013-07-18
https://github.com/square/dagger
http://blog.csdn.net/zjbpku/article/details/18732295
Butterknife介绍
http://jakewharton.github.io/butterknife/
Androidannotation介绍