Dagger2代碼實(shí)踐

這是一篇Dagger2代碼實(shí)踐的文章,寫的不錯(cuò),拿來記錄下,對自己理解幫助很大

環(huán)境配置

project: build.gradle

dependencies {
        //...

        //dagger2
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }

app: build.gradle

//...

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

android {
    //...
}

dependencies {
    //...

    //dagger2
    apt 'com.google.dagger:dagger-compiler:2.7'
    compile 'com.google.dagger:dagger:2.7'
    provided 'javax.annotation:jsr250-api:1.0'
}

開始擼代碼

首先我們要不落俗套的借用一張圖:

[圖片上傳失敗...(image-9c2eb9-1528445377332)]

來解釋一下這樣圖,通俗的理解,我們知道 Dagger2 中一個(gè)很重要的概念就是 Scope 生命周期,這里的 component(容器) 的框框可以看成一個(gè)容器,并且每個(gè)component 一般都擁有自己的 Scope, module 可以看成生產(chǎn)物品并放入 component中的工廠(雖然它不叫 factory)。并且框框里面的component 可以使用外層 component 中的產(chǎn)品。

結(jié)合圖:ApplicationComponent 是一個(gè)容器,它的 ApplicationModule 負(fù)責(zé)生產(chǎn)一些產(chǎn)品。里層的 ActivityComponent 在 ApplicationComponent中,并享有 ApplicationComponent中的所有產(chǎn)品,并且自己也有 ActivityModule 可以生產(chǎn)自己的產(chǎn)品。 然后再里面就是 UserComponent,它擁有ApplicationComponent和ActivityComponent中的所有產(chǎn)品,并自己也有 Module。這里的產(chǎn)品,就是我們可以用@Inject 注入的東西

其中有兩個(gè)生命周期 @Singleton 和 @PerActivity(自定義),@Singleton并不是我們設(shè)計(jì)模式中的單例模式,而是Dagger2中為了保持一個(gè)產(chǎn)品在一個(gè)Scope中只有一個(gè)對象的標(biāo)簽。@PerActivity 也一樣,表示在 @PerActivity 這個(gè)生命周期中,只包含一個(gè)這樣產(chǎn)品的標(biāo)簽。

Dagger2 本來只是一個(gè)依賴注入框架,再簡單不過了。但是非要搞出這么復(fù)雜的結(jié)構(gòu)是為了什么?其實(shí)這是根據(jù)Android開發(fā)的特點(diǎn)來的。Fragment 依賴 Activity 依賴 Application 其實(shí)本來就有這樣的結(jié)構(gòu),如果你按照這種思想來理解,會容易很多。

好了,上代碼

首先建立最大的 AppComponent

AppComponent.Java

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {

    Context getContext();

    ToastUtil getToastUtil();
}

注意這里的 再 Component中給出的 ToastUtil 是提供給依賴于 AppComponent 的所有 Component的。這里表示所有依賴它的結(jié)構(gòu)都可以使用 toast 功能。

AppModule.java

@Module
public class AppModule {

    private Context context;

    public AppModule(Context context) {
        this.context = context;
    }

    @Singleton
    @Provides
    public Context provideContext() {
        return this.context;
    }

    @Singleton
    @Provides
    public ToastUtil provideToastUtil(Context context) {
        return new ToastUtil(context);
    }
}

Module中由 @Provides 修飾的表示我這個(gè)Component中能提供的產(chǎn)品(供外界注入)。

ToastUtil.java
很簡單

public class ToastUtil {

    private Context context;

    public ToastUtil(Context context) {
        this.context = context;
    }

    public void showToast(String message) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }
}

自定義 App,并將 AppComponent 初始化:
App.java

public class App extends Application {

    private AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();

    }

    public AppComponent getAppComponent() {
        return this.appComponent;
    }
}

別忘了修改 AndroidManifest.xml 文件 <application android:name=".App">...</application>

以上就是最大的生命周期 App,而且內(nèi)容都是 @Singleton 的。然后我們來寫 Acitivyt的。

嚴(yán)格按照圖上的來寫:

寫一個(gè)抽象的 ActivityComponent

ActivityComponent.java

@PerActivity
@Component(modules = {ActivityModule.class}, dependencies = {AppComponent.class})
public interface ActivityComponent {

    Activity getActivity();

    ToastUtil getToastUtil();
}

ActivityModule.java

@Module
public class ActivityModule {

    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @PerActivity
    @Provides
    public Activity provideActivity() {
        return this.activity;
    }
}

這里注意兩點(diǎn):
1. ActivityComponent 中 還需要重寫一次 ToastUtil getToastUtil();, 上面我們提到,Component中寫的是 可提供給依賴自己的Component的東西,并且不能直接繼承自 AppComponent。這里只需要提供接口,然后它自己會找到AppComponent中并獲得ToastUtil

  1. @PerActivity,如果使用了 dependencies,那么依賴的一方的 Scope 不能和 父級 相同,其實(shí)@PerActivity的代碼和 @Singleton 是一樣的,只是需要我們自己重新定義一下而已。

PerActivity.java

@Scope
@Documented
@Retention(RUNTIME)
public @interface PerActivity {
}

然后寫一個(gè) BaseActivity ,它的主要作用其實(shí)就是提供 ActivityComponent,因?yàn)槔^承自它的Activity都需要這個(gè)容器。

BaseActivity.java

public class BaseActivity extends AppCompatActivity {

    private ActivityComponent activityComponent;

    public ActivityComponent getActivityComponent() {
        return activityComponent;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        activityComponent = DaggerActivityComponent.builder()
                .appComponent(((App) getApplication()).getAppComponent())
                .activityModule(new ActivityModule(this))
                .build();
    }
}

上面完成了 AppComponent 和 一個(gè)抽象的 BaseActivity,為什么叫它抽象,因?yàn)槟悴粫盟鼇韺?shí)例化一個(gè)Activity吧,它只是為了給所有 具體的Activity模塊提供內(nèi)容。當(dāng)然,還有一種寫法,就是不用BaseActivity這個(gè)東西,直接讓具體的每個(gè)Activity和Application發(fā)生關(guān)系。至于怎么寫,我們放在后面吧!

具體的MainComponent

MainComponent.java

@MainActivityScope
@Component(dependencies = {ActivityComponent.class}, modules = {MainModule.class})
public interface MainComponent {

    void inject(MainActivity mainActivity);

    MainFragmentComponent mainFragmentComponent();
}

MainModule.java

@Module
public class MainModule {

    @Provides
    public UserRepository provideUserRepository() {
        return new UserRepository();
    }
}

MainFragment.java

@MainActivityScope
@Subcomponent
public interface MainFragmentComponent {

    void inject(MainFragment mainFragment);
}

這里注意幾點(diǎn):

  1. void inject(MainActivity mainActivity);void inject(MainFragment mainFragment); 因?yàn)橐途唧w的依賴組件發(fā)生關(guān)聯(lián),所以添加了注入接口。
  2. 關(guān)于 @Subcomponent 的用法,它的作用和 dependencys 相似,這里表示 FragmentComponent 是一個(gè)子組件,那么它的父組件是誰呢? 提供了獲取 MainFragmentComponent 的組件,如 MainComponent中的 MainFragmentComponent mainFragmentComponent();,是這樣做的關(guān)聯(lián)。
  3. MainModule中提供了 UserRepository,表示要給數(shù)據(jù)倉庫,這里只是模擬數(shù)據(jù)。

UserRepository .java

public class UserRepository  {

    public UserRepository() {
    }

    public User getUser() {
        User user = new User();
        user.name = "yxm";
        return user;
    }
}

User.java

public class User {
    public String name;
}
  1. 自定義的 Scope,和前面方法一樣,直接copy @Singleton 注解中的代碼
    MainActivityScope.java
@Scope
@Documented
@Retention(RUNTIME)
public @interface MainActivityScope {
}

MainActivity和MainFragment怎么注入

MainActivity.java

public class MainActivity extends BaseActivity {

    private MainComponent mainComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mainComponent = DaggerMainComponent.builder()
                .activityComponent(getActivityComponent())
                .mainModule(new MainModule())
                .build();
        mainComponent.inject(this);
    }

    public MainComponent getMainComponent() {
        return this.mainComponent;
    }
}

還是最流行的MVP模式:

MainFragmentContact.java

public class MainFragmentContact {
    public interface View {
        void setUserName(String name);

        void showToast(String msg);
    }

    public static class Presenter {
        public UserRepository userRepository;

        @Inject
        public Presenter(UserRepository repository) {
            this.userRepository = repository;
        }

        private View view;

        public void setView(View view) {
            this.view = view;
        }

        public void toastButtonClick() {
            String msg = "hello world";
            view.showToast(msg);
        }

        public void userInfoButtonClick() {
            User userData = this.userRepository.getUser();
            this.view.setUserName((userData.name));
        }
    }
}

然后是Fragment

MainFragment.java

public class MainFragment extends Fragment implements MainFragmentContact.View {

    @Inject
    MainFragmentContact.Presenter mainPresenter;
    @Inject
    ToastUtil toastUtil;

    private MainFragmentComponent mainFragmentComponent;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (getActivity() instanceof MainActivity) {
            mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();
            mainFragmentComponent.inject(this);

        }
        mainPresenter.setView(this);
    }

    @Nullable
    @Override
    public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        android.view.View view = inflater.inflate(R.layout.fragment_main, container, false);
        return view;
    }

    @Override
    public void onViewCreated(android.view.View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button btnToast = (Button) view.findViewById(R.id.btn_toast);
        btnToast.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View view) {
                mainPresenter.toastButtonClick();
            }
        });

        Button btnUserData = (Button) view.findViewById(R.id.btn_user_info);
        btnUserData.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View view) {
                mainPresenter.userInfoButtonClick();
            }
        });
    }

    @Override
    public void setUserName(String name) {
        ((TextView) getView().findViewById(R.id.et_user)).setText(name);
    }

    @Override
    public void showToast(String msg) {
        toastUtil.showToast(msg);
    }
}

代碼有點(diǎn)長,注意幾點(diǎn):

  1. 與MainComponent關(guān)聯(lián)上
if (getActivity() instanceof MainActivity) {
    mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();
    mainFragmentComponent.inject(this);
}
  1. 內(nèi)部類注入時(shí),必須使用 static 的,如上的 MainContact 中的 Presenter

  2. 其他的就是MVP方面的知識了,如果不懂,就自己看看唄。然后還有 layout文件,我不信你不會寫,哈哈哈

另一種寫法

剛剛我們提到,還可以讓Activity直接和Application發(fā)生關(guān)系,怎么寫呢?

  1. ActivityComponent 不依賴 AppComponent,所以也不能再提供ToastUtil

ActivityComponent.java

@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
    Activity getActivity();
}
  1. MainComponent直接依賴Appcomponent

MainComponent.java

@MainActivityScope
@Component(dependencies = {AppComponent.class}, modules = {MainModule.class, ActivityModule.class})
public interface MainComponent {

    void inject(MainActivity mainActivity);

    MainFragmentComponent mainFragmentComponent();
}
  1. BaseActivity中提供 AppComponent的引用,因?yàn)樗幸⑷階ppComponent的Activity都需要這個(gè),所以 寫在BaseActivity中,同時(shí) ActivityComponent也不需要了。

AppCompatActivity.java

public class BaseActivity extends AppCompatActivity {

    public AppComponent getAppComponent() {
        return ((App) getApplication()).getAppComponent();
    }
}
  1. 注入的地方,添加 AppComponent,同時(shí)添加 ActivityModule和MainModule

BaseActivity.java

public class MainActivity extends BaseActivity {

    private MainComponent mainComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mainComponent = DaggerMainComponent.builder()
                .appComponent(getAppComponent())
                .activityModule(new ActivityModule(this))
                .mainModule(new MainModule())
                .build();
        mainComponent.inject(this);
    }

    public MainComponent getMainComponent() {
        return this.mainComponent;
    }
}

好了,其他代碼不用動,直接可以運(yùn)行

總結(jié)

如果看完并理解了這篇,你肯定能更加理解Dagger2的設(shè)計(jì)思想。這種分層的結(jié)構(gòu),可以讓我們的項(xiàng)目結(jié)構(gòu)化更加清晰。不要看著上面的代碼很復(fù)雜,實(shí)現(xiàn)的內(nèi)容又有限。其實(shí),這個(gè)架子搭好了,以后往里面加?xùn)|西就方便了。

現(xiàn)在就可以在各個(gè)層級添加自己想要注入的對象的 provideXXX 方法,然后在Activity或者Fragment中直接注入,十分方便,你可以自己體會體會。

當(dāng)前我們注入的數(shù)據(jù)源是實(shí)體類,其實(shí)完全可以注入一個(gè)interface,例如 UserFromNet 和 UserFromLocal 表示來自網(wǎng)絡(luò)和本地的數(shù)據(jù)源,通過注解來更換數(shù)據(jù)源。這也是面相抽象編程的思想。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 部分內(nèi)容參考自:[Android]使用Dagger 2依賴注入 - DI介紹(翻譯)[Android]使用Dagg...
    AItsuki閱讀 48,132評論 66 356
  • 提前準(zhǔn)備 如果你對Dagger2一點(diǎn)基礎(chǔ)都沒有,建議你先看看第一篇:Dagger2入門詳解 如果想直接看代碼,可以...
    DakerYi閱讀 1,321評論 3 26
  • 什么的依賴注入 在軟件工程中,依賴注入是實(shí)現(xiàn)控制反轉(zhuǎn)的方式之一。百度百科中對于控制反轉(zhuǎn)的解釋如下:控制反轉(zhuǎn)(Inv...
    小甜李子閱讀 1,789評論 5 3
  • Github鏈接:(https://github.com/BoxingWoo/bilibili) 前言 本項(xiàng)目的所...
    BoxingWoo閱讀 2,282評論 0 11
  • 。她是母儀天下的靜魅皇后,是前朝太尉韓瑜欽之女,韓家的大小姐,她亦是長安城內(nèi)無人不知無人不曉的四大美人之首韓鳶璃,...
    溺水三千i閱讀 398評論 0 0

友情鏈接更多精彩內(nèi)容