在GithubBrowserSample中對於Dagger有一些進階的用法,包括自動inject到Activity/Fragment中,以及使用Module封裝ViewModelFactory讓往後新增ViewModel時不用再修改ViewModelFactory,這兩天就來將這兩種方式加入我們的程式中。
當我們要在Activity或Fragment使用DI的時候都要呼叫inject(this)
,由於每次都要重複這個動作,Google就寫了一個AppInjector搭配一些程式小修改來自動處理這件事。
首先必須標明Activity/Fragment是否有用到DI,建立一個interface Injectable:
/**
* Marks an activity / fragment injectable.
*/
public interface Injectable {
}
因為只是單純的標示用途所以裡面不用任何method。
接著就在我們要用DI的Activity及Fragment加上Injectable:
public class MainActivity extends AppCompatActivity implements Injectable,
HasSupportFragmentInjector {
...
}
public class RepoFragment extends Fragment implements Injectable {
...
}
建立AppInjector:
/**
* Helper class to automatically inject fragments if they implement {@link Injectable}.
*/
public class AppInjector {
private AppInjector() {}
public static void init(GithubApp githubApp) {
DaggerAppComponent.builder()
.application(githubApp)
.build()
.inject(githubApp);
githubApp
.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity,
Bundle savedInstanceState) {
handleActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity,
Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private static void handleActivity(Activity activity) {
if (activity instanceof Injectable || activity instanceof HasSupportFragmentInjector) {
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentPreAttached(FragmentManager fm,
Fragment f,
Context context) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
init(GithubApp githubApp)
中的DaggerAppComponent那段跟我們原本在GithubApp中的一樣,只是改成在這呼叫而已。
從Activity開始著手,我們用registerActivityLifecycleCallbacks
註冊每一個Activity的生命週期callback,在onActivityCreated
的時候呼叫handleActivity(activity)
,此method會檢查Activity是否為Injectable或HasSupportFragmentInjector,若是的話執行inject。
Fragment的部分,在handleActivity(activity)
會檢查Activity是否為FragmentActivity,若是的話註冊其下Fragment的生命週期callback,並在onFragmentPreAttached
檢查Fragment是否為Injectable以執行inject。
註1:google sample中只用HasSupportFragmentInjector判斷Activity是否有用到DI,這在該sample中沒有問題,但我們若有其他Activity是本身有使用DI但其下沒有Fragment或Fragment沒有使用DI的話將導致條件式不成立而沒有執行inject;我在Activity加上Injectable並在檢查式加上activity instanceof Injectable,讓只有Activity本身用到DI時也會自動inject。
註2:對於Fragment的處理,google sample是在onFragmentCreated
中執行inject,我依照Dagger官方文件的說明改成在onFragmentPreAttached
執行。
註3:一堆中英文夾雜看起來很亂,但其實內容不難且我盡力修飾了,給點機會。
最後在Application中初始化AppInjector就完成了:
public class GithubApp extends Application implements HasActivityInjector {
...
@Override
public void onCreate() {
super.onCreate();
...
AppInjector.init(this);
}
...
}
接著就可以把Activity和Fragment中的inject(this)刪掉,只要implement Injectable就好囉。
GitHub source code:
https://github.com/IvanBean/ITBon2018/tree/day11-dagger-auto-inject