آشنایی با dagger 2 در اندروید – قسمت اول


آشنایی با فریم ورک تزریق وابستگی در اندروید

ادامه بررسی Dagger 2 در اندروید

آموزش و نحوه پیاده سازی dagger 2 موضوع یکی از مقالات پیشین بلاگ بود. در این قسمت به ادامه این مطلب می پردازیم.

ساخت نمونه جدید

باید به روش خاصی این کار را درون کلاسی از نوع Application انجام دهیم. چرا که این نمونه گیری باید تنها یک بار در طول برنامه انجام شود.

public class MyApp extends Application {

    private AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        
        // Dagger%COMPONENT_NAME%
        mAppComponent = DaggerAppComponent.builder()
                // list of modules that are part of this component need to be created here too
                .appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
                .netModule(new NetModule("https://api.github.com"))
                .build();

        // If a Dagger 2 component does not have any constructor arguments for any of its modules,
        // then we can use .create() as a shortcut instead:
        //  mAppComponent = com.codepath.dagger.components.DaggerAppComponent.create();
    }

    public AppComponent getAppComponent() {
       return mAppComponent;
    }
}

اگر نمی توانید ارجاعی به مولفه Dagger بزنید حتما پروژه خود را rebuild کنید.

چون کلاس MyApp از Application ارث بری می کند باید در مانیفست، در تگ application آنرا مشخص نماییم.

<application
      android:allowBackup="true"
      android:name=".MyApp">

 

در اکتیویتی مربوطه باید بتوانیم به سادگی به این مولفه ها دسترسی داشته باشیم و متد inject() را صدا کنیم.

public class MyActivity extends Activity {
  @Inject OkHttpClient mOkHttpClient;
  @Inject SharedPreferences sharedPreferences;

  public void onCreate(Bundle savedInstance) {
        // assign singleton instances to fields
        // We need to cast to `MyApp` in order to get the right method
        ((MyApp) getApplication()).getAppComponent().inject(this);
    } 

انواع مورد نیاز

اگر به دو object متفاوت از یک نوع نیاز داشته باشیم، می توانیم از حاشیه نویسی @Named استفاده کنیم. باید در هر دو قسمت از کد که یکی singleton ها تعریف می شود و دیگری قسمتی که از آنها استفاده می گردد.

@Provides @Named("cached")
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
    OkHttpClient client = new OkHttpClient();
    client.setCache(cache);
    return client;
}

@Provides @Named("non_cached") @Singleton
OkHttpClient provideOkHttpClient() {
    OkHttpClient client = new OkHttpClient();
    return client;
}

 

تزریق این بخش ها به کد، به این حاشیه نویسی ها هم نیاز دارد :

@Inject @Named("cached") OkHttpClient client;
@Inject @Named("non_cached") OkHttpClient client2;

حاشیه نویسی @Named در خود dagger تعریف شده است اما می توان حاشیه نویسی های دیگری که مد نظر برنامه می باشد را نیز به وجود آورد :

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface DefaultPreferences {
}

 

اسکوپ ها

در dagger 2 می توان کپسوله سازی مولفه ها را به واسطه تعریف اسکوپ ها تعیین نمود. برای مثال می توان اسکوپی ساخت که تنها به اندازه در طول حیات یک اکتیویتی یا فرگمنت زنده بماند. می توان اسکوپی ساخت که به اندازه یک session اعتبارسنجی زنده باشد. به هر تعداد دلخواه می توان به این ترتیب اسکوپ هایی را برای طول عمر قسمت های مختلف کد ساخت. حاشیه نویسی مورد نیاز برای این کار @interface ست:

@Scope
@Documented
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyActivityScope
{
}

گرچه dagger 2 در هنگام اجرای برنامه و runtime به حاشیه نویسی ها اعتنایی ندارد، برای سهولت بررسی های آتی کد این کار را انجام می دهیم.

مولفه های وابسته در مقابل زیر مولفه ها

اعمال نفوذ اسکوپ ها مولفه های وابسته یا زیرمولفه ها را بسازیم. اگر در برنامه  عناصر متفاوتی وجود داشته باشند که لازم نباشد در تمام طول برنامه برقرار باشند. (عناصری که چرخه حیات آنها وابسته به چرخه زندگی فرگمنت ها یا اکتیویتی ها باشد). در این حالت می توان عناصر وابسته یا زیرمولفه ها را تعریف نمود.

از طرفی هم مولفه های وابسته و هم زیرمولفه ها، هر کدام روشی را برای کپسوله سازی کد فراهم می نمایند.

عناصر وابسته به مولفه والدی نیاز دارند تا از طریق آن بتوانند وابستگی های خود را لیست کنند، در حالی که زیر مولفه ها این گونه نیستند. برای عناصر والد نیاز به نمایش عناصری که در سلسله پایین تر آن قرار دارند، می باشد. به این شکل که نوع و متدها در این روند مشخص می شوند :

// parent component
@Singleton
@Component(modules={AppModule.class, NetModule.class})
public interface AppComponent {
    // remove injection methods if downstream modules will perform injection

    // downstream components need these exposed
    // the method name does not matter, only the return type
    Retrofit retrofit(); 
    OkHttpClient okHttpClient();
    SharedPreferences sharedPreferences();
}

اگر نوشتن این خط کد فراموش شود، بخش هدف برای injection تعیین نمی شود و در نهایت خطایی با همین مضمون خواهید گرفت. همانگونه که سطح دسترسی فیلدها مدیریت می شود، استفاده از عناصر والد امکان کنترل دقیق تر و کپسوله سازی بهتری را فراهم می آورد. اما استفاده از زیر مولفه ها (sub components) تزریق وابستگی ها را آسان تر می نماید.

  • دو مولفه وابسته نباید در یک اسکوپ تعریف شوند. برای نمونه، دو عنصر در یک اسکوپ، هر دو نمی توانند @Singleton را داشته باشند. هر مولفه وابسته به یک اسکوپ مستقل برای تعریف نیاز دارد.
  • در حالی که dagger 2 امکان ساخت نمونه های محصور در یک اسکوپ را فراهم آورده، مسئولیت ایجاد و حذف رفرنس ها بر عهده برنامه نویس می باشد.

عناصر وابسته

فرض کنید در برنامه لازم است تا session مربوط به ورود کاربر را داشته باشیم، می توانیم اسکوپی را به شکل زیر برای ساخت عنصری که طول عمرش به اندازه همین session است می سازیم :

import java.lang.annotation.Retention;
import javax.inject.Scope;

@Scope
public @interface UserScope {
}

در قدم بعدی عنصر والد را به این شکل تعریف می نماییم :

@Singleton
@Component(modules={AppModule.class, NetModule.class})
public interface AppComponent {
    // downstream components need these exposed with the return type
    // method name does not really matter
    Retrofit retrofit();
}

حال می توان عنصر فرزند را تعریف نمود :

@UserScope // using the previously defined scope, note that @Singleton will not work
@Component(dependencies = AppComponent.class, modules = GitHubModule.class)
public interface UserComponent {
    void inject(MainActivity activity);
}

فرض کنید خروجی ماژول زیر یک اینترفیس (واسط) API باشد :

@Module
public class GitHubModule {

    public interface GitHubApiInterface {
      @GET("/org/{orgName}/repos")
      Call<List<Repository>> getRepository(@Path("orgName") String orgName);
    }

    @Provides
    @UserScope // needs to be consistent with the component scope
    public GitHubApiInterface providesGitHubInterface(Retrofit retrofit) {
        return retrofit.create(GitHubApiInterface.class);
    }
}

برای دسترسی به نمونه Retrofit باید این عناصر را مولفه رده بالا آنها ( که این نمونه به آنها وابسته است) تعریف شود.

@Singleton
@Component(modules={AppModule.class, NetModule.class})
public interface AppComponent {
    // remove injection methods if downstream modules will perform injection

    // downstream components need these exposed
    Retrofit retrofit();
    OkHttpClient okHttpClient();
    SharedPreferences sharedPreferences();
}

قدم نهایی استفاده از UserComponent برای ساخت نمونه جدید است. این بار باید ابتدا AppComponent را بسازیم سپس، این مقدار را به متد سازنده constructor)) کلاس DaggerUserComponent پاس دهیم.

AppComponent mAppComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule("https://api.github.com"))
                .build();

UserComponent userComponent = DaggerUserComponent.builder()
                .appComponent(mAppComponent)
                .gitHubModule(new GitHubModule()) // this is optional
                .build();

برای درک بهتر می توانید این سورس در گیت هاب را مطالعه نمایید.

 

زیر مولفه ها (Subcomponents)

استفاده از subcomponents روش دیگری برای ارث بری وابستگی یک عنصر است. شبیه عناصر وابسته، subcomponents چرخه حیات مستقلی دارند. هم چنین garbage collector می تواند ارجاعات آنها را در زمان لازم حذف کند. یک مزیت این روش این مورد است که نیازی نیست عناصر رده پایین همگی تعریف شوند.

تفاوت مهم دیگر این است که subcomponent ها نیاز دارند به سادگی در عنصر والد تعریف شوند.

در این قسمت مثالی برای چگونگی بکارگیری subcomponent ها در یک اکتیویتی در نظر می گیریم. این کلاس با حاشیه نویسی @Subcomponent مشخص گردیده است :

@MyActivityScope
@Subcomponent(modules={ MyActivityModule.class })
public interface MyActivitySubComponent {
    void inject(MyActivity activity);
}

ماژولی که استفاده می شود در این قسمت نوشته شده است :

@Module
public class MyActivityModule {
    private final MyActivity activity;

    // must be instantiated with an activity
    public MyActivityModule(MyActivity activity) { this.activity = activity; }
   
    @Provides @MyActivityScope @Named("my_list")
    public ArrayAdapter providesMyListAdapter() {
        return new ArrayAdapter<String>(activity, android.R.layout.my_list);
    }
    ...
}

در آخر، در عنصر والد، متد factory را با مقدار بازگشتی از نوع عنصر و وابستگی های لازم برای ساخت نمونه جدید تعریف می کنیم :

@Singleton
@Component(modules={ ... })
public interface MyApplicationComponent {
    // injection targets here

    // factory method to instantiate the subcomponent defined here (passing in the module instance)
    MyActivitySubComponent newMyActivitySubcomponent(MyActivityModule activityModule);
}

در مثال بالا، نمونه جدیدی از subcomponent هر بار که newMyActivitySubcomponent() صدا زده شود، ساخته خواهد شد.

public class MyActivity extends Activity {
  @Inject ArrayAdapter arrayAdapter;

  public void onCreate(Bundle savedInstance) {
        // assign singleton instances to fields
        // We need to cast to `MyApp` in order to get the right method
        ((MyApp) getApplication()).getApplicationComponent())
            .newMyActivitySubcomponent(new MyActivityModule(this))
            .inject(this);
    } 
}

ProGuard

Dagger 2 باید بدون نیاز به ProGuard کار کند، اما اگر library class dagger.producers.monitoring.int را مشاهده نمودید، مطمئن شوید که در تنظیمات gradle به جای provided مورد annotationProcessor تعریف شده باشد.

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز / 5. تعداد:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image