DI这块接触的比较少,大部分印象还停留在Spring的XML配置。Dagger2刚接触时理解上有点吃力,实话,一般的项目使用Dagger2比较费力,代码会变的比较复杂,但是移动平台上实现注入、分层解耦,Dagger2是个不错的框架。
首先,如何引入?
project build.gradle中需要加入以下内容:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' //这里需要加入apt引入
}
}
如果基于maven仓库可修改仓库url如下:
allprojects {
repositories {
mavenCentral()
maven{
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
}
app build.gradle中,先引入一个插件:
apply plugin: 'com.neenbedankt.android-apt'
添加依赖:
apt 'com.google.dagger:dagger-compiler:2.0'
compile 'com.google.dagger:dagger:2.0'
provided 'javax.annotation:jsr250-api:1.0' //java标注
这里会使用jave中的注解,需要引入java annotation。
rxjava retrofit okhttp gson butterknife引入:
compile 'io.reactivex:rxjava:1.0.16'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.6.0'
compile 'com.jakewharton:butterknife:7.0.1'
配置完毕。
注入过程:@Module标注的类视为可以被注入对象的声明类;@Component注入组件,将申明的可注入对象注入到相应的对象,@Provides可注入对象声明,表述不清楚,这里类似于Spring Boot的配置,实例:
@Module
@Singleton
public class ApiModule {
@Provides
public OkHttpClient provideOkHttpClient() {
return new OkHttpClient();
}
@Provides
public Retrofit provideRetrofit(OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(URLUtils.MAP_URL)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Provides
protected ApiService provideApiService(Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
}
以上ApiModule中声明了三个方法(@Singleton表示注入对象以单例提供),这里分别表示三个可注入的对象声明:OkHttpClient、Retrofit和ApiService(@Provides为注入对象注解,类似@Bean),相当于XML文件中的Bean标签声明一样,如果方法中带有参数,如:
provideRetrofit(OkHttpClient okHttpClient)
则该参数必须是已经声明的注入对象,如果参数需要从外部传入则需要在注入时提供,例子:
@Module
@Singleton
public class MainModule {
private MainView mainView;
public MainModule(MainView mainView){
this.mainView = mainView;
}
@Provides
public MainPresenter providesMainPresenter(){
return new MainPresenterImpl(mainView);
}
}
这里注入MainPresenter时需要提供MainView作为构造参数,但是MainView并不是声明的注入对象,所以在MainModule构造中进行传值,直接调用,注意注入时的写法:
component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();
component.inject(this);
上面在Activity中注入MainModule时需要指明MainModule的构造参数(这里Activity实现了MainView接口,DaggerMainComponent为Component:MainComponent编译后自动生成的注入对象,通过该对象实例化MainComponent),所以添加了mainModule(new MainModule(this)),如果没有构造参数则直接书写:
component = DaggerMainComponent.builder().build();
component.inject(this);
Component定义:
@Component(dependencies = ApplicationComponent.class, modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
LocationUtils locationUtils();
}
参数dependencies表示当前component依赖的其他component,modules表示当前component注入的哪些module中定义的可注入对象。方法inject的参数定义当前component可注入到哪个类对象(这里的参数不支持接口,这里参数为MainView将无法完成注入),LocationUtils是注入的一个工具类,定义:
public class LocationUtils {
@Inject
public LocationUtils() {
}
public Location getLocation(Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(context.LOCATION_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Location location = locationManager.getLastKnownLocation(locationManager.GPS_PROVIDER);
if (location == null) {
location = locationManager.getLastKnownLocation(locationManager.NETWORK_PROVIDER);
}
return location;
}
return null;
}
}
这里@Inject注解和Module中@Provides有相同的功能,我们需要将自定义的工具了或者数据库操作类声明为可注入对象可以用这种方式,在相应的Component直接声明,如上:ActivityComponent。
MainActivity定义:
public class MainActivity extends AppCompatActivity implements MainView {
@Bind({R.id.place_1,R.id.place_2,R.id.place_3,R.id.place_4,R.id.place_5,R.id.place_6,R.id.place_7})
List<TextView> places;
private MainComponent component;
@Inject
protected MainPresenter mainPresenter;
@Inject
protected LocationUtils locationUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();
component.inject(this);
Location location = locationUtils.getLocation(this);
Map<String,Object> sub_params = new HashMap<>();
sub_params.put("latlng", location.getLatitude() + "," + location.getLongitude());
sub_params.put("sensor", true);
sub_params.put("language", "zh-CN");
mainPresenter.loadEntity(sub_params);
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
mainPresenter.cancleLoad();
}
@OnClick({R.id.place_1, R.id.place_2, R.id.place_3, R.id.place_4, R.id.place_5, R.id.place_6, R.id.place_7})
public void onClick(View view) {
String msg = "";
switch (view.getId()) {
case R.id.place_1:
msg = places.get(0).getText().toString();
break;
case R.id.place_2:
msg = places.get(1).getText().toString();
break;
case R.id.place_3:
msg = places.get(2).getText().toString();
break;
case R.id.place_4:
msg = places.get(3).getText().toString();
break;
case R.id.place_5:
msg = places.get(4).getText().toString();
break;
case R.id.place_6:
msg = places.get(5).getText().toString();
break;
case R.id.place_7:
msg = places.get(6).getText().toString();
break;
}
Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
}
@Override
public void setPlaces(MapResult result) {
if (result != null) {
int i = 0;
for (TextView t:places){
if (i == result.results.get(0).address_components.size()){
break;
}
t.setText(result.results.get(0).address_components.get(i).short_name);
i++;
}
}
}
}
结构:Module中定义的是各个Provides即可注入对象,Component声明了该组件可注入那些Module中定义的Provides。
实例:https://github.com/buobao/Android_framework.git