這是一篇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
- @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):
-
void inject(MainActivity mainActivity);和void inject(MainFragment mainFragment);因?yàn)橐途唧w的依賴組件發(fā)生關(guān)聯(lián),所以添加了注入接口。 - 關(guān)于 @Subcomponent 的用法,它的作用和 dependencys 相似,這里表示 FragmentComponent 是一個(gè)子組件,那么它的父組件是誰呢? 提供了獲取 MainFragmentComponent 的組件,如 MainComponent中的
MainFragmentComponent mainFragmentComponent();,是這樣做的關(guān)聯(lián)。 - 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;
}
- 自定義的 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):
- 與MainComponent關(guān)聯(lián)上
if (getActivity() instanceof MainActivity) {
mainFragmentComponent = ((MainActivity) getActivity()).getMainComponent().mainFragmentComponent();
mainFragmentComponent.inject(this);
}
內(nèi)部類注入時(shí),必須使用 static 的,如上的 MainContact 中的 Presenter
其他的就是MVP方面的知識了,如果不懂,就自己看看唄。然后還有 layout文件,我不信你不會寫,哈哈哈
另一種寫法
剛剛我們提到,還可以讓Activity直接和Application發(fā)生關(guān)系,怎么寫呢?
- ActivityComponent 不依賴 AppComponent,所以也不能再提供ToastUtil
ActivityComponent.java
@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
Activity getActivity();
}
- MainComponent直接依賴Appcomponent
MainComponent.java
@MainActivityScope
@Component(dependencies = {AppComponent.class}, modules = {MainModule.class, ActivityModule.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
MainFragmentComponent mainFragmentComponent();
}
- BaseActivity中提供 AppComponent的引用,因?yàn)樗幸⑷階ppComponent的Activity都需要這個(gè),所以 寫在BaseActivity中,同時(shí) ActivityComponent也不需要了。
AppCompatActivity.java
public class BaseActivity extends AppCompatActivity {
public AppComponent getAppComponent() {
return ((App) getApplication()).getAppComponent();
}
}
- 注入的地方,添加 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ù)源。這也是面相抽象編程的思想。