سه نکتهی کاربردی در dagger2 (قسمت اول)
خیلی وقت بود در مورد dagger2 (دگر۲) چیزی نخونده بودم تا اینکه دوباره فرصت شد مقالههای جدیدی بخونم و ۳ تا نکتهی جدید ازشون یاد بگیرم. این سه نکته رو در قالب دو پُست بلاگ توضیح میدم. برای اینکه کاربردشون رو بهتر نشون بدم، هر نکته رو در قالب یک کامیت روی پروژهی SearchMovies اعمال کردم.
نکتهی اول- استفاده از Component.Builder@ و BindsInstance@
بصورت پیشفرض دگر برای هر کامپوننت یک Builder تولید میکنه. گاهی لازم میشه خودمون دگر رو برای ساخت Builderیی که نیاز داریم راهنمایی کنیم، بطور مثال در پروژهی SearchMovies کلاس AndroidModule (ماژولی که Objectهای مرتبط با سیستمعامل اندروید رو برای ما فراهم میکنه) نیاز به Context داره. یکی از راههایی که میشه Context رو به کلاس AndroidModule پاس داد، از طریق سازنده کلاسش هست. مثل کد زیر:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@Module public class AndroidModule { private SearchMovieApplication searchMovieApplication; public AndroidModule(SearchMovieApplication searchMovieApplication) { this.searchMovieApplication = searchMovieApplication; } @Provides @Singleton public Context provideContext() { return searchMovieApplication.getApplicationContext(); } @Provides @Singleton public Resources provideResources() { return searchMovieApplication.getResources(); } @Provides @Singleton public SharedPreferences provideSharedPreferences() { return PreferenceManager.getDefaultSharedPreferences(searchMovieApplication); } } |
ولی داکیومنت دگر توصیه کرده به جای روش بالا از شخصی سازی Builder کامپوننت و BindsInstance@ استفاده بشه. با این دوتا میشه Context رو به Object Graph وصل کرد. یعنی اگر ماژولی نیاز به Context داره، لازم نیست شبیه کد بالا از سازنده ماژول استفاده کرد. برای اینکه توصیه دگر پیاده کنیم باید از BindsInstance@ در داخل اینترفیس Builder کامپوننت استفاده کنیم. یادتون باشه داخل اینترفیس Builder باید حتما یک متد باشه که پارامتر ورودی نداشته باشه و خود کامپوننت رو Return بکنه. کد ApplicationComponent بعد اضافه شدن Builder شخصی سازی شده:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Singleton @Component( modules = { AndroidModule.class, ApplicationModule.class, ApiModule.class, InteractorModule.class, ClientModule.class, }) public interface ApplicationComponent { MovieDetailComponent plus(MovieDetailPresenterModule module); MovieSearchComponent plus(MovieSearchPresenterModule module); @Component.Builder interface Builder { ApplicationComponent build(); @BindsInstance Builder application(SearchMovieApplication application); } } |
زمانی که کد کامپوننت به شکل بالا تغییر کرد، میشه کد کلاس AndroidModule رو هم اصلاح کرد. به این صورت که دیگه از سازنده خبری نباشه و هر متدی که برای فراهم کردن Objectش نیاز به Context داره، کافیه Context رو از پارامتر ورودی متد بگیره. مثل زیر:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@Module public class AndroidModule { @Provides @Singleton public Context provideContext(SearchMovieApplication application) { return application.getApplicationContext(); } @Provides @Singleton public Resources provideResources(SearchMovieApplication application) { return application.getResources(); } @Provides @Singleton public SharedPreferences provideSharedPreferences(SearchMovieApplication application) { return PreferenceManager.getDefaultSharedPreferences(application); } } |
الان اگر پروژه رو بیلد کنید، دگر برای ApplicationComponent یک Builder میسازه که شامل متد application() هست و بصورت زیر میشه ازش استفاده کرد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class SearchMovieApplication extends Application { @Override public void onCreate() { super.onCreate(); component = DaggerApplicationComponent.builder() .application(this) .build(); } private static ApplicationComponent component; public static ApplicationComponent getComponent() { return component; } } |
کلا کاربرد BindsInstance@ در جایی هست که نیاز باشه Objectیی رو که قبل از ساخت کامپوننتی وجود داره به Object Graph اون کامپوننت وصل کرد. توی اینجا من Context رو مثال زدم اما میتونه چیزهای دیگهای هم باشه.