View on GitHub

Wills' blog

  • home | github | resume |
  • 安卓dagger2

    04 Feb 2016

    前言

    什么是依赖性。简单地说依赖性指代码里面两个模块(在面向对象语语言里面就是两个类)之间的耦合关系,一般是指一个雷要使用另外一个类做一些事情。

    从高层(使用者)到低层(被使用)的依赖关系是危险的。原因是一旦我们想要改变其中的某个模块,我们需要修改双方的代码。解决这个问题的方法就是反向依赖。

    Dependency Injection(依赖性注入)简称DI,著名的有square开发的dagger1以及google开发的dagger2.

    In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies.
    A dependency is an object that can be used (a service). 
    An injection is the passing of a dependency to a dependent object (a client) that would use it. 
    The service is made part of the client's state. Passing the service to the client,
    rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
    

    这是wiki上关于依赖性注入的解释:依赖性注入是一种实现了依赖性关系反转约束的软件设计模式。依赖性是一个可以被使用的对象(服务)。注入则是吧一个依赖性传递给一个可能使用这种依赖性的对象(客户端)。这个服务被当成是客户端的状态。把服务传给客户端,而不是允许客户端生成或者找到这个服务,是这个模式的关键需求。

    依赖性注入允许软件设计遵循依赖反转原则。客户端委托外部代码(注入者)提供给他依赖性的关系。客户端不允许调用注入者的代码。注入代码构造了服务然后让客户端注入他们。这意味着客户端不需要知道注入代码的具体实现,不需要知道服务的结构,不需要知道他具体是使用了哪个具体的服务。客户端只要知道服务接口。

    关于依赖性的定义,有下面一个例子简单的解释了。

    Android: Dagger2でDIをする. 基本編 Part1

    依赖性注入有什么好处呢?这边文章里面给出了解释(链接)

    就像butteknife库能够简单的对view,handler和资源进行访问一样,Dagger2提供了获取共享实例引用的方法。例如,一旦我们定义了一个比如MyTwitterApiClient或者对SharedPrefence,可能还有dbAccessor,我们能够通过简单定义一个@Inject注解

    public class MainActivity extends Activity {
       @Inject MyTwitterApiClient mTwitterApiClient;
       @Inject SharedPreferences sharedPreferences;
    
       public void onCreate(Bundle savedInstance) {
           // assign singleton instances to fields
           InjectorClass.inject(this);
       }
    

    你的对象的创建是有一个隐式的顺序的,Dagger2通过遍历依赖关系图生成能够方便的理解和追踪的代码,这样大大简化了你手动的编写代码来获取引用以及将他传递给依赖他的对象。同样也方便你对模块的重构,因为你能够专注于模块内的逻辑,不需要去想模块应该按照怎么样的顺序创建出来。

    依赖注入不仅能够管理整个应用生命周期的实例,同时也能定义一个在较短的生命周期中存在的实例,利于用户的session和activity的生命周期等。

    然而Dagger2的缺点就是,学习成本太高,理解太困难了。。。。

    下面的例子是一个在软件内全局实例的例子,

    OkHttpClient client = new OkHttpClient();
    
    // Enable caching for OkHttp
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(getApplication().getCacheDir(), cacheSize);
    client.setCache(cache);
    
    // Used for caching authentication tokens
    SharedPreferences sharedPrefeences = PreferenceManager.getDefaultSharedPreferences(this);
    
    // Instantiate Gson
    Gson gson = new GsonBuilder().create();
    GsonConverterFactory converterFactory = GsonConverterFactory.create(Gson);
    
    // Build Retrofit
    Retrofit retrofit = new Retrofit.Builder()
                                    .baseUrl("https://api.github.com")
                                    .addConverterFactory(converterFactory)
                                    .client(client)  // custom client
                                    .build();
    

    这些例子在使用的时候,都可以通过注入来简化代码和逻辑。

    Dagger2

    Dagger2有以下一些特征:

    @Inject

    依赖性声明。

    @Module

    声明提供依赖性方法的类。

    @Provide

    放在模块内,声明对Dagger提供怎样的依赖性。

    @Component

    @Inject和@Module之间的桥,提供被定义的所有类型的接口。

    @Scope

    @Qualifier

    如何一步步导入Dagger2

    Step1. gradle

    在build.gradle中追加用于生成注入代码的配置:

     buildscript {
         repositories {
             jcenter()
         }
         dependencies {
             classpath 'com.android.tools.build:gradle:1.3.0'
    +        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
         }
     }
    

    在app/build.gradle中导入dagger2的库

     apply plugin: 'com.android.application'
    +apply plugin: 'com.neenbedankt.android-apt'
    
     android {
         compileSdkVersion 23
         buildToolsVersion "23.0.1"
    
         defaultConfig {
             applicationId "com.sample.dagger2"
             minSdkVersion 15
             targetSdkVersion 23
             versionCode 1
             versionName "1.0"
         }
         buildTypes {
             release {
                 minifyEnabled false
                 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
             }
         }
     }
    
     dependencies {
         compile fileTree(dir: 'libs', include: ['*.jar'])
         testCompile 'junit:junit:4.12'
         compile 'com.android.support:appcompat-v7:23.1.0'
         compile 'de.greenrobot:eventbus:2.4.0'
         compile 'com.mcxiaoke.volley:library:1.0.19'
         compile 'com.jakewharton:butterknife:7.0.1'
    
    +    compile 'com.google.dagger:dagger:2.0.2'
    +    apt 'com.google.dagger:dagger-compiler:2.0.2'
    +    provided 'org.glassfish:javax.annotation:10.0-b28'
     }
    

    Step2. 制作Module类

    Module类是包含@Provides方法的类,@Provides方法的返回值就是被注入的时候的实例。例如,@Inject Context context的时候就调用下面provideApplicationContext方法。

    命名规范是@Provides方法以provide单词开头,模块的名字以Module结尾。

    // AppModule.java
    import android.app.Application;
    import android.content.Context;
    
    import javax.inject.Singleton;
    
    import dagger.Module;
    import dagger.Provides;
    
    @Module
    public class AppModule {
        private final Application application;
    
        public AppModule(Application application) {
            this.application = application;
        }
    
        @Provides
        @Singleton
        Context provideApplicationContext() {
            return application.getApplicationContext();
        }
    }
    

    每个layer单独生成一个模块类比较清晰一点。@Provides方法的参数由被注入的实例提供。

    // InfraLayerModule.java
    
    @Module
    public class InfraLayerModule {
        @Provides
        public WeatherRepository provideWeatherRepository(Context context) {
            return new WeatherRepository(context);
        }
    }
    
    // AppLayerModule.java
    
    @Module
    public class AppLayerModule {
        @Provides
        public WeatherFacade provideWeatherFacade(WeatherRepository repository) {
            return new WeatherFacade(repository);
        }
    }
    

    Step3. Component接口

    Dagger1和Dagger2的区别的一点就是Component类。Component是从Dagger的ObjectGraph演变过来的,不过这都不重要了。Dagger2中,Component的实现类是自动生成的。

    Component类是为了@Inject声明的时候注入类被@Module里的@Provides声明的方法的返回实例的里面实行注入而产生的类。

    // AppComponent.java
    import javax.inject.Singleton;
    
    import dagger.Component;
    import ko2ic.dagger2.application.AppLayerModule;
    import ko2ic.dagger2.infrastructure.InfraLayerModule;
    import ko2ic.dagger2.ui.activity.SecondActivity;
    
    @Singleton
    @Component(modules = {InfraLayerModule.class, AppLayerModule.class, AppModule.class})
    public interface AppComponent {
        void inject(SecondActivity activity);
    }
    

    Step4. Application子类

    生成Application类的子类,这是为了能在Activity中构造Component。

    // AppApplication.java
    public class AppApplication extends Application {
    
        private AppComponent applicationComponent;
    
        @Override
        public void onCreate() {
            super.onCreate();
            initializeInjector();
        }
    
        private void initializeInjector() {
            applicationComponent = DaggerAppComponent.builder()
                    .appModule(new AppModule(this))
                    .build();
        }
    
        public AppComponent getApplicationComponent() {
            return applicationComponent;
        }
    }
    

    Step5. 依赖性注入

    把有实例的地方都变成依赖性注入。

     public class SecondActivity extends AppCompatActivity {
    
    -     private WeatherFacade facade; 
    +     @Inject
    +     WeatherFacade facade;
    
          @Bind(R.id.editText)
          EditText editText;
    
          @Bind(R.id.button)
          Button button;
    
          @Override
          protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_second);
             ButterKnife.bind(this);
    +        getApplicationComponent().inject(this);
             button.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
    -                 facade = new WeatherFacade(getApplicationContext());
                      facade.fetchWeahter(editText.getText().toString());
                 }
             });
         }
    +     private AppComponent getApplicationComponent() {
    +         return ((AppApplication) getApplication()).getApplicationComponent();
    +    }
     public class WeatherFacade {
    
         private WeatherRepository repository;
    
    -    private Context context;
    
    -    public WeatherFacade(Context context){
    -        this.context = context;
    +    @Inject
    +    public WeatherFacade(WeatherRepository repository){
    +        this.repository = repository;
         }
    
         public void fetchWeahter(String cityCode){
    -        repository = new WeatherRepository(context);
             repository.fetchWeather(cityCode);
         }
     }
     public class WeatherRepository {
    
         private Context mContext;
    
    +    @Inject
         public WeatherRepository(Context context) {
             mContext = context;
         }
    

    另外,关于Field Injection和Constractor Injection这两者方式,可以参考这里, 不做过深的探讨。

    参考文档


    [click to comment]