Lesson1
What is Dependency Injection?
將依賴直接傳遞給類,而不是由類來初始化依賴
開發者的代碼低耦合,且能夠容易地進行測試
低耦合:令互相依賴降到最小,的對象間可以交互,但不清楚彼此的細節
How to install
apt 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.dagger:dagger:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
|
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
|
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
|
Use
example by simple example:
I'm going to work with two classes, Vehicle and Motor. Motor is the independent class and Vehicle is the dependent class. I'm going to start creating this model within a new package called model.
public class Motor {
private int rpm;
public Motor(){
this.rpm = 0;
}
public int getRpm(){
return rpm;
}
public void accelerate(int value){
rpm = rpm + value;
}
public void brake(){
rpm = 0;
}
}
|
public class Vehicle {
private Motor motor;
public Vehicle(Motor motor){
this.motor = motor;
}
public void increaseSpeed(int value){
motor.accelerate(value);
}
public void stop(){
motor.brake();
}
public int getSpeed(){
return motor.getRpm();
}
}
|
we do not create a motor in the Vehicle, we just pass it into, because we want 降低耦合,Vehicle just need to know it has a motor,do not ned to know all the thing of the motor(if create a motor in the Vehicle,need know all about the motor)
now ,start use Dragger2!!!!
to create a class with the @Module annotation. This class is going to provide the objects you will need with its dependencies satisfied.
we need Motor and Vehicle,so
@Module
public class VehicleModule {
@Provides @Singleton
Motor provideMotor(){
return new Motor();
}
@Provides @Singleton
Vehicle provideVehicle(){
return new Vehicle(new Motor());
}
}
|
The @Singleton annotation indicates that there will be only one instance of the object.
@Inject
public Vehicle(Motor motor){
this.motor = motor;
}
|
Vehicle need a motor,and we already create a provider to provide the motor, so add @Inject to the method, @Inject annotation to request dependencies in the constructor, fields, or methods
but! how the Module and InJect acn connect together???How can we get the Vehicle?
now,we want Inject the Motor into the Vehicle,so we need VehicleModule.class(to provide the Motor,) inject into the Vehicle,need a provideVehicle in Component
@Component
we know the get Vehicle method in the VehicleModule
@Singleton
@Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
}
|
than we can use @Component Interface to Obtain Objects
When you try to create a new object of the interface with the @Component annotation, you have to do it using the prefix Dagger_<NameOfTheComponentInterface>, in this case Dagger_VehicleComponent, and then use the builder method to call every module within.
VehicleComponent component = Dagger_VehicleComponent.builder().vehicleModule(new VehicleModule()).build();
vehicle = component.provideVehicle();
|
Try in Android
create a module that provide the context
@Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides
@Singleton
public Context provideContext() {
return application;
}
@Provides
@Singleton
public SharedPreferences provideSharedPreferences(){
return PreferenceManager.getDefaultSharedPreferences(application);
}
}
|
Lesson2
Inject to the target
aim:we want we can get the SharedPreferences by dragger2 at Main Activity
fro mlesson 2 we already have this:
@Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides
@Singleton
public Context provideContext() {
return application;
}
@Provides
@Singleton
public SharedPreferences provideSharedPreferences(){
return PreferenceManager.getDefaultSharedPreferences(application);
}
}
|
we can provide the SharedPreferences ,How can we give this to the MainActivity?
1.create the component interface
@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MyApplication target);
void inject(MainActivity target);
}
|
because we want to use in the MainActivity, so void inject(MainActivity target); must have
2.create ApplicationComponent
to create the ApplicationComponent ,we need ApplicationModule instance,the ApplicationModule need a context,so we create the ApplicationComponent in the Application
public class MyApplication extends Application {
private ApplicationComponent component;
public ApplicationComponent getComponent() {
return component;
}
@Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
}
}
|
3 Inject in to MainActivityt
public class MainActivity extends AppCompatActivity {
@Inject SharedPreferences prefs;
((MyApplication)getApplication()).getComponent().inject(this);
|
Lesson3
How to perform constructor injection
How to perform method injection
How to provide named injections
How to require & perform named injections
How to create multiple Dagger modules
How and why it is important to rely on abstractions (interfaces)
How to provide a Retrofit RestAdpater
How to build a custom Retrofit Endpoint and inject it into a Restadapter
How a multi-module Dagger object graph is composed
API KEy:f2333815f5f8c240bcc9baf188f6540a
when we see the code,we can fins that in CurrentConditionService
has
@Inject ForecastService forecastService;
and
@Override
public void onCreate() {
super.onCreate();
((TaskoApplication)getApplication()).getComponent().inject(this);
}
it use Dragger 2 to inject here
ForecastService is a interface
so we need to add new Provider
into the ApplicationModule
@Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides @Singleton
public Context provideContext() {
return application;
}
@Provides @Singleton
public SharedPreferences provideSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
@Provides
public ForecastService provideForecastService(RestAdapter restAdapter) {
return new ForecastServiceImpl(restAdapter);
}
}
|
The New Module
@Module
public class ApiModule {
@Provides
@Named(Constants.Injection.Named.FORECAST_API_KEY)
public String provideForecastIOApiKey(Context context) {
return context.getString(R.string.forecast_io_api_key);
}
@Provides
public Endpoint provideEndpoint(@Named(Constants.Injection.Named.FORECAST_API_KEY) String apiKey) {
return new ForecastIOApiEndpoint().setApiKey(apiKey);
}
@Provides
@Singleton
public RestAdapter provideRestAdapter(Endpoint endpoint) {
return new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(endpoint)
.build();
}
}
|
we cen see that here is a @Named it ensure the
provideEndpoint ge the string form provideForecastIOApiKey
MultiModule
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.apiModule(new ApiModule())
.build();
|
inject to the new target
@Singleton
@Component(modules = { ApplicationModule.class, ApiModule.class })
public interface ApplicationComponent {
void inject(TaskoApplication target);
void inject(MainActivity target);
void inject(CurrentConditionService target);
void inject(MainFragment target);
}
|
The module get from the module
@Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides @Singleton
public Context provideContext() {
return application;
}
@Provides @Singleton
public SharedPreferences provideSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
@Provides
public ForecastService provideForecastService(RestAdapter restAdapter) {
return new ForecastServiceImpl(restAdapter);
}
}
|
provideSharedPreferences get from provideContext
@Module
public class ApiModule {
@Provides
@Named(Constants.Injection.Named.FORECAST_API_KEY)
public String provideForecastIOApiKey(Context context) {
return context.getString(R.string.forecast_io_api_key);
}
@Provides
public Endpoint provideEndpoint(@Named(Constants.Injection.Named.FORECAST_API_KEY) String apiKey) {
return new ForecastIOApiEndpoint().setApiKey(apiKey);
}
@Provides
@Singleton
public RestAdapter provideRestAdapter(Endpoint endpoint) {
return new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(endpoint)
.build();
}
}
|
provideRestAdapter get from provideEndpoint get from provideForecastIOApiKey get from provideContext
all is auto do by Dagger2
-----------------------------------------------------------------------------------------------------------------------
Lesson 5
Testing with Dagger 2 and Mockito
see how to use the Mockito:https://www.blogger.com/blogger.g?blogID=1723759563624018420#editor/target=post;postID=1259495158126674147;onPublishedMenu=overview;onClosedMenu=overview;postNum=0;src=postname
Espresso,need this
How can we test it with Dagger 2 and Mockito
Aim:check will the view show the wanted data?
use the Mockito to Mock the ForecastService,than use Espresso to check it
Below show how to achieve this:
first ,we need to create the Modeuse model ,it retur the Mocked ForecastService
@Module
public class MockDemoModule {
@Provides @Singleton
public ForecastService providesForecastService() {
return Mockito.mock(ForecastService.class);
}
}
|
second,because has a new Module ,we need to create a new Component
@Singleton
@Component(modules = { MockDemoModule.class })
public interface TestingComponent extends DemoComponent {
void inject(MainActivityTest target);
}
|
MainActivityTest is the testing class
third,in the lesson 2 and 3 app,we create the instance of the component in the Application,in the test,we create the component in the Application,but we need to create a specificed Application for test,and hold the test component.
public class MockDemoApplication extends DemoApplication {
@Override
public DemoComponent createComponent() {
return DaggerTestingComponent.builder()
.mockDemoModule(new MockDemoModule()).build();
}
}
|
how to make the Android use this component when testing?
set it in AndroidJUnitRunner
public class MockDemoTestRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, MockDemoApplication.class.getName(), context);
}
}
|
at the build.gradle,set to use MockDemoTestRunner'
defaultConfig {
applicationId "io.caster.daggertesting"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner 'io.caster.daggertesting.MockDemoTestRunner'
}
|
…..later ,learn Espresso first
otheruseful thing
----------------------------------------------------------------------------------------------------------------------------
Dagger 2 Scopes 1 and 2
@Singleton
means there will be one instance of the object for the lifetime of the component, not for the lifetime of the JVM.Fo the hole Application
How do define our scope?
if no scope,injector create an instance,use for one,than forget it,if have scope,injector will reuse the instancein later injection.
Let we create s a scope only available for the profile fragment.
first,define the Scope name
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ProfileScope {
}
|
Second,use it
@ProfileScope
@Subcomponent(modules = { ProfileModule.class })
public interface ProfileComponent {
void inject(ProfileFragment target);
}
|
@Module
public class ProfileModule {
// Profile Screen based dependencies go here
@Provides
public SomeBigObject providesSomeBigObject() {
return new SomeBigObject();
}
}
|
that mean the it will reuse only in the Profile.
subcompoent mean :
inherits the bindings from a parent,
at here,the parent component is Appcomponent
so
@Singleton
@Component(modules = {AppModule.class, AndroidModule.class})
public interface AppComponent {
void inject(DemoApplication target);
ProfileComponent plus(ProfileModule ProfileModule);
SettingsComponent plus(SettingsModule settingsModule);
}
|
how can we get the ProfileComponent to use?
at Application se tt he get method first:
public ProfileComponent createProfileComponent() {
profileComponent = appComponent.plus(new ProfileModule());
return profileComponent;
}
|
and we need tomange the life cycle by ourself,in application,create this method
public void releaseProfileComponent() {
profileComponent = null;
}
|
than call at the ProfileFragment,because we want it only scope at the ProfileFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoApplication) getActivity().getApplication()).createProfileComponent().inject(this);
}
|
when destory,release
@Override
public void onDestroy() {
super.onDestroy();
((DemoApplication) getActivity().getApplication()).releaseProfileComponent();
}
|
The different component:
when require the sub component ,the parent component will together,sho can use two or more component at the same time,they can have different scope.