App framework implementation -- dagger2

Short book address: http://www.jianshu.com/p/519dc63e5297

github address: https://github.com/jiahongfei/ArrowEngine

Since the company's App architecture needs to be adjusted, the App framework is being packaged recently. Please click here to give the github address first .
The composition of the framework is MVP+Dagger2+RxJava+Retrofit+OkHttp+RxCache+单元测试(Junit+Mockito)currently only the simplest packaged version, and the framework will be maintained in the future, and I hope everyone will support it.

This article first introduces the Dagger2 Dependency Injection framework (Dependency Injection)

Dagger2 directory.png

The dagger2 dependency injection below uses the DI shorthand instead.

Dagger2 keywords

@Inject

Used to mark a field, indicating that this field requires DI to provide an instance.

Used to mark the constructor, indicating that this class can be used to provide DI instances. That is, provide @Injectan instance of the decorated field. The marked constructor has parameters, then the parameters are provided by the DI instance.

@InjectConstructors of marker classes cannot be overloaded. That is, there can only be one constructor.

The following code example:

public class BaseActivity<T extends IPresenter> extends AppCompatActivity {
    ......
    //用来标记字段,表示mPresenter需要DI来提供实例
    @Inject
    protected T mPresenter;
    ......
}

public class DataRepositoryManager implements IDataRepositoryManager {
    ......
    private Retrofit retrofit;
    private RxCache rxCache;
    //标记构造方法,表示DataRepositoryManager类可以提供DI实例
    //retrofit和rxCache需要DI来提供实例
    //@Inject标记构造方法之后,这个类只能有一个构造方法,不能重载
    @Inject
    public DataRepositoryManager(Retrofit retrofit, RxCache rxCache){
        this.retrofit = retrofit;
        this.rxCache = rxCache;
    }
    ......
}
@Module

Used to mark the class, which is used to provide DI instances. That is, provide instances to @Injectmarked fields or to marked constructor parameters. @Inject(Only marked @Providesmethods can provide DI instances, see below)

@Provides

Used to mark methods. In @Modulemarked classes, only @Providesmarked methods can provide DI instances. All other methods are normal methods.

There are two ways to provide a DI instance, the first is the @Injectmarked constructor, the second is in the @Modulemarked class, only @Providesthe marked method

@Module and @Provides the following code

@Singleton
//这个标记表示这个类中的方法可以提供DI实例
@Module
public class DataRepositoryModule {
     ......
    @Singleton
    //这个标记表示这个方法可以提供DI实例,也就是提供RxCache。
    //提供上文中DataRepositoryManager构造方法的RxCache参数
    //同时这个方法的参数也是需要DI实例
    @Provides
    RxCache providerRxCache(Application application, File cacheDir, @Nullable RxCacheConfig rxCacheConfig) {
        RxCache.Builder builder = new RxCache.Builder();

        if (null != rxCacheConfig) {
            rxCacheConfig.configRxCache(application, builder);
        }
        return builder.persistence(cacheDir, new GsonSpeaker());
    }
    ......
}
@Component

It is a bridge of communication between @Moduleand , it assigns the instance returned by the marked method in the class to the marked field or the parameter of the marked constructor.@Inject@Module@Provides@Inject@Inject

The following code example:

@Singleton
//modules表示DI实例是由ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class类中的@Provides方法提供
@Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class})
public interface AppComponent {
    //这个方法必须写,表示AppDelegateManager类中需要的DI实例由ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class类中的@Provides方法提供
    void inject(AppDelegateManager delegate);
    //由于@Component标记的类可以依赖"dependencies"(也可以理解为类的继承),所以想要暴露的方法就要在这个类中定义,这些方法必须都是"modules"提供的方法,如下方法可以在依赖的类中使用
    Application getApplication();
    RxCache getRxCache();
    Retrofit getRetrofit();
    ......
}

@ActivityScope
//LoginComponent继承了AppComponent.class中提供的方法,也就是上面代码中,声明的方法
@Component(dependencies = AppComponent.class, modules = LoginModule.class)
public interface LoginComponent {
    void inject(LoginActivity loginActivity);

}
@Qualifier

Qualifier: Used to mark methods. If @Providesthe return values ​​of the marked methods are the same , @Qualifierthe defined annotations must be used to distinguish which method is used to return the DI instance, otherwise the compilation will fail.

In @Injectthe field of the @Injecttag or the parameter of the constructor of the tag, use @Qualifierthe defined annotation to distinguish the required DI instance.

The following code example:

//限定符用来定义注解,这个注解提供本地Mock数据
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MockData {
}

@Singleton
@Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class})
public interface AppComponent {
    void inject(AppDelegateManager delegate);
    ......
    //如下两个方法的返回值类型相同,如果不用@Qualifier进行标记,是无法通过编译的,
    //原因是由@Inject标记的字段无法找到准确的方法来提供DI实例。
    IDataRepositoryManager providerRepositoryManager();
    @MockData
    IDataRepositoryManager providerMockRepositoryManager();
    ......
}

@Singleton
@Module
public class DataRepositoryModule {
    ......
     //如下两个方法的返回值类型相同,如果不用@Qualifier进行标记,是无法通过编译的,
     //原因是由@Inject标记的字段无法找到准确的方法来提供DI实例。
    @Singleton
    @Provides
    public IDataRepositoryManager providerRepositoryManager(Retrofit retrofit, RxCache rxCache) {
        return new DataRepositoryManager(retrofit, rxCache);
    }

    @Singleton
    @Provides
    @MockData
    public IDataRepositoryManager providerMockRepositoryManager(Application application, @Nullable DataRepositoryModule.MockDataConfig mockDataConfig) {
        return new MockDataRespsitoryManager(application,mockDataConfig);
    }
    ......
}

public class LoginInteractorImpl extends BaseModel implements LoginContract.LoginInteractor {
    private ServiceApi serviceApi;
    //下面构造方法的参数由DI来提供,@MockData限定符表示选择上面代码片段的providerMockRepositoryManager方法来提供DI实例,
    //如果去掉@MockData限定符表示使用上面代码片段的providerRepositoryManager来提供DI实例
    @Inject
    public LoginInteractorImpl(@MockData IDataRepositoryManager repositoryManager) {
        super(repositoryManager);
        serviceApi = repositoryManager.getRepositoryDataService(ServiceApi.class);
    }
    @Override
    public Observable<ApiResponse<UserLogin>> getUserLogin(String phone, String smsCode, String blockBox, String extendParam) {
        return serviceApi.getUserLogin(phone,smsCode,blockBox,extendParam);
    }
    ......
}
@Named(“name”)

The usage scenario is exactly @Qualifierthe same, but it is distinguished by the string in "()".

@Singleton

Scope: used to mark classes or methods. Literally, everyone will think of the singleton pattern. If a class is annotated with this annotation, the class will become a singleton? The answer is no, that is, this annotation will not make the class into a singleton. It must be remembered that it is just a mark , indicating that the implementation of this class is a singleton. There is only one in the App, and the specific singleton code still needs to be written by yourself.

The following code example:

//只是标记这个类要被实现成单例,需要自己保证类的对象在App声明周期内只有一个
@Singleton
@Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class})
public interface AppComponent {
    void inject(AppDelegateManager delegate);
    ......
}

//用这个类来保证AppComponent在App声明周期只有一个实例,单例
public class AppDelegateManager {
    private AppComponent appComponent;
    private static final AppDelegateManager sAppDelegateManager = new AppDelegateManager();
    public static final AppDelegateManager getInstance() {
        return sAppDelegateManager;
    }
    protected AppDelegateManager() {
    }
    public final void init(Application application, AppDelegateConfig appDelegateConfig) {
        appComponent = DaggerAppComponent
                .builder()
                .applicationModule(new ApplicationModule(application))
                .dataRepositoryModule(new DataRepositoryModule())
                .appDelegateConfig(appDelegateConfig)
                .build();
        appComponent.inject(this);
    }
    public AppComponent getAppComponent() {
        return appComponent;
    }
}

//在自定义的Application类中初始化AppDelegateManager,来保证在App声明周期内只有一个AppComponent实例
public class CustomApplication extends Application implements IApp {
    ......
    @Override
    public void onCreate() {
        super.onCreate();
        AppDelegateConfig appDelegateConfig = new AppDelegateConfig
                .Builder()
                .setBaseUrl("http://www.weather.com.cn/adat/sk/")
                .setCacheDir(getCacheDir())
                .builder();
        AppDelegateManager.getInstance().init(this, appDelegateConfig);
    }
    @Override
    public AppComponent getAppComponent() {
        return AppDelegateManager.getInstance().getAppComponent();
    }
    ......
}
@Scope

Custom scope annotation: His role is also to enable programmers to intuitively see the scope of this class, and the others have no effect. @ComponentThe annotations in the dependencies @Scopemust be different or an error will be reported.

// 用来标识Activity作用域
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

@Singleton
@Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class})
public interface AppComponent {
    ......
    void inject(AppDelegateManager delegate);
    ......
}

//LoginComponent类依赖AppComponent类他必须用@ActivityScope来标记进行区分,否则编译报错
@ActivityScope
@Component(dependencies = AppComponent.class, modules = LoginModule.class)
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

The process of Dagger2 dependency injection

Here is a quote from Niu Xiao's great god's article

Step 1: Find out if there is a method in the Module to create the class.

Step 2: If there is a create class method, check whether the method has parameters

步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数  

步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束  

Step 3: If there is no method to create a class, look for the constructor of the Inject annotation to see if the constructor has parameters

步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数  

步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

illustration

dagger2.png

Dagger2 summary:

1. Decoupling

Decoupling is the dagger2biggest advantage. A class cannot exist alone, otherwise it will be meaningless. In this way, there will be many classes in this class that will be combined to complete the business logic. There will be many classes in this class. Yes , new classif AClassthis class has an instance in BClass、CClass、DClassmore than ...... There are more than N classes that need to be modified in each place. If you are not careful, there will be various strange bugs. More complicated is the interdependence of objects, that is, for decoupling, create only one copy of the object in the same class, and then inject the object into the required class, so that if it is modified, it will only be modified in one place to achieve decoupling. Effect.newAClassnewdagger2

To sum up one sentence, dagger2more than N are omitted new class, and only objects need to be created in one place.

2. Convenient Mock data

In the early stage of development, the client often develops business logic against the interface document, which involves reading local Mock data. What we do is to write the mockdata into jsona file and save it locally, and switch Modelthe data acquisition method in the incoming (M in MVP) class to read the local mockdata. The data acquisition method DataRepositoryis injected into it through dependency injection (DI) Model, which can be switched uniformly. very convenient.

3. Facilitate unit testing

Since the dependent objects are injected into the class through DI, it is Mockito.mockvery convenient to pass the object, and the unit test will be covered later in the article.

references:

http://www.jianshu.com/p/1d42d2e6f4a5

http://www.jianshu.com/p/65737ac39c44

http://www.jianshu.com/p/cd2c1c9f68d4

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324375589&siteId=291194637