1. 程式人生 > >dagger2的配置及基本使用(一)

dagger2的配置及基本使用(一)

一、配置:

1.1 在Project的gradle指令碼中配置

  dependencies {
        classpath  'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }

1.2在Module的gradle指令碼中配置

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
apt 'com.google.dagger:dagger-compiler:2.11'
    compile 'com.google.dagger:dagger:2.11'
}

二、程式碼實現

2.1、使用@Inject和@Component實現依賴注入

2.1.1: 在實體類的構造方法中標註@Injecct,表示其提供依賴,程式碼如下:

package testdagger.com.xn.testdagger2.modle;

import android.util.Log;
import javax.inject.Inject;

/**
 * Created by  賀謀昌 on 2017/8/1.
 */

public class Watch {
    @Inject
    public Watch() {
    }

    public
void work() { Log.e("Watch", "Dagger2 watch is work"); } }

2.1.2:建立注入器,即提供依賴和目標類之前的橋樑,是一個用@Conponent標註的介面

程式碼如下:

package testdagger.com.xn.testdagger2.component;
import dagger.Component;
import testdagger.com.xn.testdagger2.MainActivity;

/**
 * Created by  賀謀昌 on 2017/8/1.
 */
@Component
public interface MainActivityComponent { void inject(MainActivity activity); }

其建立的方法中傳入的引數為目標類的實體

2.1.3

在目標類中進行注入,並標註依賴注入的實體類

public class MainActivity extends Activity {

    @BindView(R.id.btn)
    Button btn;
    @Inject
    Watch watch;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        DaggerMainActivityComponent.create().inject(this);
    }

    @OnClick(R.id.btn)
    public void onViewClicked() {
        watch.work();
    }
}

DaggerMainActivityComponent這個類是在component類完成之後進行reBuild後自動生成的類。

2.2、使用@Module和@Provides提供依賴

如果程式碼中使用了第三方的類庫,那麼目標類所需的依賴是無法進行標註的,因為無法更改實體類的原始碼,比如一個jar包,這時我們用@Module和@Provides提供依賴。

2.2.1、新建一個標註@Module的類,並通過標註@Provides的方法返回實體類的物件,程式碼如下:

/**
 * Created by  賀謀昌 on 2017/8/1.
 */
@Module
public class GsonModule {
    @Provides
    public Gson getGson() {
        return new Gson();
    }
}

2.2.2、使用已經建立的component來管理Module,只要在類名處標註上管理的是哪個Module即可,程式碼如下:

**
 * Created by  賀謀昌 on 2017/8/1.
 */
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

2.2.3、在目標類中注入,並標註要使用的實體類,程式碼如下:

public class MainActivity extends Activity {

    @BindView(R.id.btn)
    Button btn;
    @Inject
    Watch watch;
    @Inject
    Gson gson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        DaggerMainActivityComponent.create().inject(this);
    }

    @OnClick(R.id.btn)
    public void onViewClicked() {
        watch.work();
        JSONObject object = new JSONObject();
        try {
            object.put("name", "賀賀");
            object.put("age", 24);
            Man man = gson.fromJson(object.toString(), Man.class);
            Log.e("MainActivity", man.getName());
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }
}

2.3、如果要注入的物件是抽象的則不能用@Inject來提供依賴,這時要用@Module,案例如下:

Engine是一個抽象類

public abstract class Engine {
public abstract String work();
}

MyEngine是Engine的子類

public class MyEngine extends Engine {
    @Override
    public String work() {
        return "MyEngine is working";
    }
}

Car持有Engine的物件

public class Car {
    Engine engine;

   @Inject
    public Car(Engine engine) {
        this.engine = engine;
    }

    public String run() {
        return engine.work();
    }
}

2.3.1、在component中提供一個方法,給Engine提供例項

@Module
public class GsonModule {

    @Provides
    public Engine getEngine() {
        return new MyEngine();
    }
}

2.3.2在MainActivity中注入

  @Inject
    Car car;
    ...
  DaggerMainActivityComponent.create().inject(this);
    ....
  Log.e("MainActivity",  car.run() );

2.4、@Name和Qualifier的基本使用

如果@Inject標註的多個構造或是@Provides標註的方法返回的是同一種類型的值,那麼程式就不知道使用哪一個來提供依賴,這時就會報錯,我們就可以使用@Qualifier來標註我們要使用的是哪一個,@Name是@Qualifier的一種實現。案例如下:
如果Engine的子類有兩個,除了MyEngine之外還有HerEngine,在Module中返回的Engine的例項的方法中,有兩個方法,一個返回MyEngine一個返回HerEngine,這時我們就要標註好哪個方法返回哪個例項,並且目標類需要哪個例項,可以用@name(字串)也可以自定義Qualifier,示例程式碼如下:

  //首先定義兩個自定義的Qualifier
  @Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface My {
}
....
....
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Her {
}
....
//在Module中標註好,哪個方法對應的哪個類的例項
  @Provides
    @My
    public Engine getEngine() {
        return new MyEngine();
    }

    @Provides
    @Her
    public Engine getHerEngine() {
        return new HerEngine();
    }

 // 標註好目標類需要哪個例項  
    public class Car {
    Engine engine;

    @Inject
    public Car(@Her Engine engine) {
        this.engine = engine;
    }

    public String run() {
        return engine.work();
    }
}

使用@Name來標註則更為簡單
“`
@Provides
@Named(“My”)
public Engine getEngine() {
return new MyEngine();
}

@Provides
@Named("Her")
public Engine getHerEngine() {
    return new HerEngine();
}
//在需要依賴的類中同樣使用@Name標註好
public class Car {
Engine engine;

@Inject
public Car(@Named("Her") Engine engine) {
    this.engine = engine;
}

public String run() {
    return engine.work();
}

}

2.5、Dagger2實現單例模式

2.5.1、實現區域性單例

在提供依賴的地方標註@Singleton

   @Singleton
    @Provides
    public Gson getGson() {
        return new Gson();
    }

在Module中標註@Singleton

  @Singleton
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

//這樣就完成了區域性單例

2.5.2、使用@Scope實現全域性單例模式

其實@Singleton是被@Scope標註的註解,@Scope標註實際上就是用來實現區域性單例的,它的本質就是註解的註解,只要在想要實現某個類是單例模式,只需要在其提供依賴的地方標註被@Scope標註的註解,然後在其component中標註被@Scope標註的註解,上述@Singleton就是做了這樣的操作。還有一個要注意的點就是component會每注入一次就例項化一次,所以我們只要讓Component成為單例,就可以實現全域性單例了。下面開始正式的實現:

新建一個被@Scope標註的註解

   @Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
首先在提供依賴處標註剛定義的註解
@Module
public class GsonModule {
    @ApplicationScope
    @Provides
    public Gson getGson() {
        return new Gson();
    }



然後在提供依賴處和component處標註此註解

@ApplicationScope
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);

    void inject(SecondActivity activity);
}

新建一個Application在onCreate中例項化component,這樣就能保證該component只有一個例項

public class MyApplication extends Application {
    MainActivityComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerMainActivityComponent.create();
    }

    public static MyApplication get(Context context) {
        return (MyApplication) context.getApplicationContext();
    }

    public MainActivityComponent getComponent() {
        return component;
    }
}

在需要使用的地方,用實現單例的component進行注入即可,在這裡我們分別在兩個不同對的Activity中列印該類例項的HashCode,若是相同,就說明我們成功實現了單例.
public class MainActivity extends Activity {


    @Inject
    Gson gson;

    @Inject
    Gson gson1;

    @BindView(R.id.jump)
    Button jump;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
      MyApplication.get(this).getComponent().inject(this);
    }

    @OnClick({R.id.btn})
    public void onViewClicked() {

        Log.e("MainActivity", car.run());
        Log.e("MainActivity", "gson:" + gson.hashCode() + " gson1" + gson1.hashCode());
    }


}

在另一個Activity中我們做相同的操作
public class SecondActivity extends Activity {

    @BindView(R.id.secoond_btn)
    Button secoondBtn;
    @Inject
    Gson gson;
    @Inject
    Gson gson1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        ButterKnife.bind(this);
        MyApplication.get(this).getComponent().inject(this);
    }

    @OnClick(R.id.secoond_btn)
    public void onViewClicked() {
        Log.e("SecondActivity", "gson:" + gson.hashCode()+" gson1"+gson1.hashCode());
    }
}

我的測試結果如下:
E/MainActivity: gson:1111566360 gson11111566360
E/SecondActivity: gson:1111566360 gson11111566360
可以看到四個HashCode都一樣,我們的全域性單例模式算是實現了。

還有兩個註解的使用,@dependencies和subComponent有空再說吧,是用來描述component的組織關係的。