لوگو عباس اویسی

متولد تهران، توسعه دهنده‌ی اندروید، پیگیر فوتبال. ارشد نرم افزار و علاقمند به جنبه‌های مختلف توسعه‌ی نرم افزار

  • عمومی
  • اندروید
  • جنریتور حلما
  • فریم‌ورک dagger
  • وب‌سرویس آموزشی فیلم‌ها

سه نکته‌ی کاربردی در dagger2 (قسمت اول)

مرداد ۹, ۱۳۹۶

خیلی وقت بود در مورد dagger2 (دگر۲) چیزی  نخونده بودم تا اینکه دوباره فرصت شد مقاله‌های جدیدی بخونم و ۳ تا نکته‌ی جدید ازشون یاد بگیرم. این سه نکته رو در قالب دو پُست بلاگ توضیح میدم. برای اینکه کاربردشون رو بهتر نشون بدم، هر نکته رو در قالب یک کامیت روی پروژه‌ی SearchMovies اعمال کردم.

نکته‌ی اول- استفاده از Component.Builder@ و BindsInstance@

بصورت پیشفرض دگر برای هر کامپوننت یک Builder تولید میکنه. گاهی لازم میشه خودمون دگر رو برای ساخت Builderیی که نیاز داریم راهنمایی کنیم، بطور مثال در پروژه‌ی SearchMovies کلاس AndroidModule (ماژولی که Objectهای مرتبط با سیستم‌عامل اندروید رو برای ما فراهم میکنه) نیاز به Context داره. یکی از راه‌هایی که میشه Context رو به کلاس AndroidModule پاس داد، از طریق سازنده کلاسش هست. مثل کد زیر:

Java
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 شخصی سازی شده:

Java
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 رو از پارامتر ورودی متد بگیره. مثل زیر:

Java
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() هست و بصورت زیر میشه ازش استفاده کرد:

Java
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 رو مثال زدم اما میتونه چیزهای دیگه‌ای هم باشه.