تزریق وابستگی یا Dependency Injection بسته به اندازهی پروژههای اندرویدی شما میتواند مشکل ساز باشد و برای همین Dagger مدتی هست که معرفی شده است و برنامه نویسان اندروید با فریم ورک Dagger کار کردهاند یا حداقل چیزی دربارهاش شنیدهاند. در مقاله آموزش dagger در اندروید ما با ویژگیها و کاراییهای آن آشنا میشویم. همراه ما باشید تا با آموزش Dagger در اندروید، اطلاعاتی دربارهی آن کسب کنید و بتوانید با آن کار کنید و از ویژگیهای آن بهره ببرید.
dagger در اندروید چیست؟
به طور خلاصه ابزاری برای تولید کدی جهت مدیریت وابستگی بین اشیاء مختلف است. Dagger یک Dependency Injector برای جاوا و اندروید میباشد. Dagger یک فریم ورک (Framework) برای تزریق وابستگی (Dependency Injection) میباشد که در زمان کامپایل (Compile) اجرا میشود. Dagger از کدها به هیچ عنوان در زمان اجرا (runtime) استفاده نمیکند، تمام تحلیلهای خود را در زمان کامپایل انجام میدهد و کد منبع جاوا را تولید میکند. همانطور که میدانید Dagger با استفاده از حاشیه نویسی (annotations) کار میکند، که به آن کمک میکند تا تمام کد مورد نیاز برای کار را بخواند و تولید کند. همهی حاشیه نویسیهای مختلفی که در Dagger استفاده میشود، راهی برای گفتن آن چیز هایی هست که Dagger نمیداند و برای رسیدن به هدف خود و ایجاد کدی که در غیر این صورت باید خودتان بنویسید از Annotations استفاده میشود.
تزریق وابستگی به شما مزایای زیر را ارائه میدهد:
قابلیت استفاده مجدد از کدهای نوشته شده
سهولت در اصلاح کد
سهولت در تست نویسی
تفاوتهای Dagger 1 با Dagger 2
به طور خلاصه Dagger 2 نسخهی بهبود یافتهی Dagger 1 میباشد و مشکلاتی را که درون Dagger 1 بود تا حدودی حل کرده است. مشکلات dagger 1:
Ugly generated code
Runtime graph composition
Inefficient graph creation
Partial traceability
Map-like API
و راه حلهای Dagger 2 برای مشکلهای Dagger 1:
Compile-time validation of the entire graph
Easy debugging; entirely concrete call stack for provision and creation
Fully traceable
POJO API
Performance
و از مشکلات Dagger 2 میتوان به انعطاف پذیری کم آن اشاره کرد. Dagger 2 از Annotationsهای زیر استفاده میکند :
Module@ و Provides@ : کلاسها و متد هایی را برای وابستگی به ما تحویل میدهند.
Inject@ : درخواست dependencies میباشد که قابل استفاده در field ،constructor یا method است.
Component@ : ماژولهای انتخاب شده را فعال کرده و برای انجام تزریق وابستگی استفاده میشود.
Dagger 2 از کد تولید شده برای دستیابی به فیلدها استفاده میکند. بنابراین استفاده از فیلدهای خصوصی برای تزریق field مجاز نیست.
تعریف ارائه دهندگان وابستگی ((Defining dependency providers(object providers)
اصطلاح dependency injection context معمولاً برای توصیف مجموعه اشیاء قابل تزریق استفاده میشود. در Dagger 2، کلاس هایی که با Module@ حاشیه نویسی شده اند، مسئولیت تهیهی اشیاء قابل تزریق را دارند. چنین کلاس هایی میتوانند متد هایی را که با Provides@ حاشیه نویسی شده است، تعریف کنند. اشیاء برگشت یافته از این متدها برای تزریق وابستگی در دسترس هستند. با Provides@ متدهای حاشیه نویسی میتوانند وابستگی را از طریق پارامترهای متد بیان کنند. این وابستگیها در صورت امکان توسط Dagger 2 برآورده میشوند.
شما از حاشیه نویسی Inject@ برای تعریف یک وابستگی استفاده میکنید. اگر یک Constructor را با Inject@ حاشیه نویسی کنید، Dagger 2 میتواند نمونه ای از این شی را برای تحقق وابستگیها استفاده کند. این کار برای جلوگیری از ساخت بیشتر متدهای Provides برای این اشیاء انجام شده است.
ارتباط Consumers و ارائه دهندگان
در یک Interface استفاده میشود. چنین رابطی توسط Dagger 2 برای تولید کد استفاده میشود. الگوی پایه برای کلاس تولید شده این است که، Dagger به عنوان پیشوند و پس از آن با نام رابط استفاده میشود. این یک کلاس تولید میکند که یک متد ساخته و امکان پیکربندی اشیاء را بر اساس پیکربندی داده شده فراهم میکند. متدهای تعریف شده در Interface برای دسترسی به اشیاء تولید شده در دسترس هستند.
یک Interface @Component ارتباط بین ارائه دهندهی اشیاء (ماژول ها) و اشیاء را بیان میکند که وابستگی را بیان میکند.
Scope annotations
می توانید از حاشیه نویسی Singleton@ استفاده کنید تا نشان دهید فقط باید یک نمونه از شی وجود داشته باشد.
فیلدها در Dagger
Dagger 2 به صورت خودکار فیلدها را تزریق نمیکند. همچنین نمیتواند فیلدهای خصوصی را تزریق کند. اگر میخواهید از تزریق فیلد استفاده کنید، باید روشی را در رابط Component@ خود تعریف کنید که نمونه ای از آن را به عنوان پارامتر تزریق میکند.
استفاده از Dagger در اندروید استودیو
برای فعال کردن Dagger 2 وابستگیهای زیر را به پروندهی build.gradle خود اضافه کنید.
بسیاری از مؤلفههای اندروید (Android Components)، به عنوان مثال Activities، توسط چارچوب Android انجام میشود و در کد ما نیست. این امر باعث میشود تامین وابستگی از طریق Constructorها به مؤلفههای Android دشوار شود.
اگر میخواهید از کلاسهای پکیج dagger.android مانند کلاس DaggerActivity استفاده کنید، وابستگیهای زیر را نیز به پروندهی build.gradle خود اضافه کنید و اگر میخواهید Activities components مانند Activities یا Fragments را تزریق کنید، این مورد نیز لازم است.
همانطور که مشاهده میکنید، ما فقط AndroidSupportInjectionModule ، ActivityBindingModule و ViewModelModule را در پارامتر ماژول نوشتیم. ما ماژولهای مورد نیاز دیگر را که Activity یا Fragment نیاز خواهد داشت، خواهیم نوشت.
ActivityBindingModule.java
@Module
public abstract class ActivityBindingModule {
@ContributesAndroidInjector(modules = {MainFragmentBindingModule.class})
abstract MainActivity bindMainActivity();
}
MainFragmentBindingModule.java
@Module
public abstract class MainFragmentBindingModule {
@ContributesAndroidInjector
abstract ListFragment provideListFragment();
@ContributesAndroidInjector
abstract DetailsFragment provideDetailsFragment();
}
ApplicationModule.java
@Singleton
@Module(includes = ViewModelModule.class)
public class ApplicationModule {
private static final String BASE_URL = "https://api.github.com/";
@Singleton
@Provides
static Retrofit provideRetrofit() {
return new Retrofit.Builder().baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Singleton
@Provides
static RepoService provideRetrofitService(Retrofit retrofit) {
return retrofit.create(RepoService.class);
}
}
ContextModule.java
@Module
public abstract class ContextModule {
@Binds
abstract Context provideContext(Application application);
}
ViewModelFactory کارخانهای است که ViewModelProvider.Factive را به منظور ارائهی نمونههای ViewModel به کلاسهای Fragment مصرف کننده گسترش میدهد. ما آن کلاس را با Inject ،ViewModelModule کردهایم.
ViewModelFactory.java
@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@NonNull
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ViewModel را راه اندازی میکنیم.
ListViewModel.java
public class ListViewModel extends ViewModel {
private final RepoRepository repoRepository;
private CompositeDisposable disposable;
private final MutableLiveData<List<Repo>> repos = new MutableLiveData<>();
private final MutableLiveData<Boolean> repoLoadError = new MutableLiveData<>();
private final MutableLiveData<Boolean> loading = new MutableLiveData<>();
@Inject
public ListViewModel(RepoRepository repoRepository) {
this.repoRepository = repoRepository;
disposable = new CompositeDisposable();
fetchRepos();
}
LiveData<List<Repo>> getRepos() {
return repos;
}
LiveData<Boolean> getError() {
return repoLoadError;
}
LiveData<Boolean> getLoading() {
return loading;
}
private void fetchRepos() {
loading.setValue(true);
disposable.add(repoRepository.getRepositories().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribeWith(new DisposableSingleObserver<List<Repo>>() {
@Override
public void onSuccess(List<Repo> value) {
repoLoadError.setValue(false);
repos.setValue(value);
loading.setValue(false);
}
@Override
public void onError(Throwable e) {
repoLoadError.setValue(true);
loading.setValue(false);
}
}));
}
@Override
protected void onCleared() {
super.onCleared();
if (disposable != null) {
disposable.clear();
disposable = null;
}
}
}
public class RepoListAdapter extends RecyclerView.Adapter<RepoListAdapter.RepoViewHolder>{
private RepoSelectedListener repoSelectedListener;
private final List<Repo> data = new ArrayList<>();
RepoListAdapter(ListViewModel viewModel, LifecycleOwner lifecycleOwner, RepoSelectedListener repoSelectedListener) {
this.repoSelectedListener = repoSelectedListener;
viewModel.getRepos().observe(lifecycleOwner, repos -> {
data.clear();
if (repos != null) {
data.addAll(repos);
notifyDataSetChanged();
}
});
setHasStableIds(true);
}
@NonNull
@Override
public RepoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_repo_list_item, parent, false);
return new RepoViewHolder(view, repoSelectedListener);
}
@Override
public void onBindViewHolder(@NonNull RepoViewHolder holder, int position) {
holder.bind(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
@Override
public long getItemId(int position) {
return data.get(position).id;
}
static final class RepoViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.tv_repo_name) TextView repoNameTextView;
@BindView(R.id.tv_repo_description) TextView repoDescriptionTextView;
@BindView(R.id.tv_forks) TextView forksTextView;
@BindView(R.id.tv_stars) TextView starsTextView;
private Repo repo;
RepoViewHolder(View itemView, RepoSelectedListener repoSelectedListener) {
super(itemView);
ButterKnife.bind(this, itemView);
itemView.setOnClickListener(v -> {
if(repo != null) {
repoSelectedListener.onRepoSelected(repo);
}
});
}
void bind(Repo repo) {
this.repo = repo;
repoNameTextView.setText(repo.name);
repoDescriptionTextView.setText(repo.description);
forksTextView.setText(String.valueOf(repo.forks));
starsTextView.setText(String.valueOf(repo.stars));
}
}
}
جمع بندی
در مقاله آموزش Dagger در اندروید ، با Dagger و کارایی آن آشنا شدیم و متوجه شدیم که استفاده از Dagger چه مزایایی برای توسعه اپلیکیشنهای اندرویدی دارد. اگر دربارهی این مقاله سوال و نظری دارید خوشحال میشویم که با ما و کاربران سون لرن به اشتراک بگذارید.
اگر به یادگیری بیشتر در زمینهی برنامه نویسی اندروید علاقه داری، با شرکت در دورهی آموزش برنامه نویسی اندروید در کمتر از یکسال به یک توسعهدهنده اندروید همه فن حریف تبدیل میشوی که آمادهی استخدام، دریافت پروژه و حتی پیادهسازی اپلیکیشن خودت هستی.
۱۲ دیدگاه
۲۲ مهر ۱۴۰۱، ۰۳:۲۲
قبل از اینکه اینجا رو بخونم، مقداری از منبع اصلی خونده بودم، ولی با خوندن اینجا باعث شد کلا اون مقداری که از منبع اصلی هم فهمیده بودم هم بپره
آخه این چه طرز توضیح دادنه
دیفالت گوگل ترنسلیت بهتر ترجمه میکرد.
همایون احمدی۱۵ مهر ۱۴۰۰، ۱۵:۴۱
سلام ممنون از آموزش جامع و خوبتون. کتابخانه ButterKnife ، اخیرا deprecate شده و بهتره از دیتا بایندینگ (Data Binding) استفاده بشه که هم خیلی راحت تره هم خوانایی کد رو افزایش میده.
سعید۲۲ خرداد ۱۴۰۰، ۲۱:۱۶
مثالی که در آخر بحث آوردین خیلی پیچیدهتر از سطح توضیحات اولیه مقالس. بیشتر قسمتهای مربوط به مثال اصلا دلیلی برای استفاده از کلاسهای خاص داده نشده
مقاله خیلی کلی و بد جمع بندی شده
نه توضیحات اولیه تکمیله خیلی سطحی و گذرا و نه بخشهای مهم مثال توضیحی داره که بشه فهمید اصلا برای چی این مدلی جلو رفته چرا فلان کلاس رو استفاده میکنه و ...
علیرضا۲۶ اردیبهشت ۱۴۰۰، ۲۱:۴۴
درسته که مبحث پیشرفته ای هستش ولی گفتن اینکه دقیقا dagger چیه و کجا کاربرد داره رو نمیشه انقدر پیچیده کردش! اصلا متوجه مطلب نشدم، حس میکنم کلا کار رو سپردید به گوگل ترنسلیت!
کامنتای دیگه رو میخوندم زیر همشون اضافه کردید مبحث پیشرفته ای هستش نیازه خودتون یه بار کدارو اجرا کنید و درک کنید، بجز این پاسخ، ممنون میشم اگر یک جواب واضح یا منبعی واضح راجب اینکه dagger دقیقا چی هست بهم بدید.
نازنین کریمی مقدم۲۷ اردیبهشت ۱۴۰۰، ۲۰:۵۱
درود.
اگر کامنت آخر که در جواب امیر عزیز رو خونده باشید، درمورد اینکه dagger دقیقا چیه و چجوری استفاده میشه توضیح دادم. اما باز در این کامنت تکرار میکنم.
در اصول بهینه کدنویسی معمولا مطرح میشه که بهتره که هیچ کلاسی از کلاس دیگری شی یا نمونه ای نسازه و بجاش بیاد شی مورد نیاز خودش رو از یک کلاس واسط که به همین منظور ساخته شده است دریافت بکنه. به استفاده از این کلاس واسط تزریق وابستگی میگیم و dagger یکی از ابزارهایی هست که این کار رو برامون انجام میده. پس در اصل کتابخانه dagger به منظور تزریق وابستگی استفاده میشه.
برای چگونگی دقیقش هم کد رو گذاشتیم.
امیر۲۲ اردیبهشت ۱۴۰۰، ۱۵:۲۱
اصلا متجه نمیشم دلیل استفاده چی میتونه باشه و اگه استفاده نشه چه اتفاقی میفته ؟ و به کارگیریش همراه mvvm چجوری هست
نازنین کریمی مقدم۲۴ اردیبهشت ۱۴۰۰، ۲۳:۱۱
درود
ببینید در اصول بهینه کدنویسی معمولا مطرح میشه که بهتره که هیچ کلاسی از کلاس دیگری شی یا نمونه ای نسازه و بجاش بیاد شی مورد نیاز خودش رو از یک کلاس واسط که به همین منظور ساخته شده است دریافت بکنه. به استفاده از این کلاس واسط تزریق وابستگی میگیم و dagger یکی از ابزارهایی هست که این کار رو برامون انجام میده.
ما یه مثال از dagger و mvvm در مقاله زدیم، با این حال برای فهم دقیقتر توصیه میکنم چندین نمونه پیاده سازی در اینترنت رو ببینید. مبحث کاملا پیشرفته هست و تمرین و دانش خوبی رو میطلبه.
هادی۳۰ فروردین ۱۴۰۰، ۰۷:۲۳
سلام خسته نباشید
صرفا خاستم یه تشکر کنم بابت مطالبی که گذاشتید تویه سایتتون،نسبت به سایتهای فارسیه دیگه که دیدم خیلی خیلی بهتر و واضح تره آموزش هاتون،البته برا کسی که دانشی از برنامه نویسی اندروید داره،قسمت معماری MVVM رو خیلی حال کردم
مسعود۲۵ فروردین ۱۴۰۰، ۰۴:۲۹
فقط يه مقاله از لغت به لغت و خيلي آماتور ترجمه شده. مثل يه كار ترجمه دانش آموزي ميمونه(نه حتي دانشجويي) كه فقط ميخواسته نمره بگيره..
نازنین کریمی مقدم۲۵ فروردین ۱۴۰۰، ۱۲:۱۵
درود
سطح این مقاله پیشرفته هست و زمان نسبتا خوبی رو باید برای درک کدها بگذارید و خودتون یکبار به صورت عملی انجام بدید تا بهتر متوجه شید. اگر جایی از مقاله رو متوجه نشدید، ما در بخش کامنتها به سوالات تون جواب میدیم و اگر محتوایی باید به مقاله اضافه بشه، حتما اون رو ویرایش میکنیم.
اصغر۱۹ دی ۱۳۹۹، ۱۵:۴۴
واقعا کارتون خیلی ضعیف هست
استفاده از گوگل ترنسلیت و نوشتن صرفا یه پست
قول میدم حتی خودتون هم یکبار از روش نخوندید
درسته پست رایگان هست، و نباید شما رو سرزنش کرد، ولی انجام ندادن بعضی کارها از انجام دادنشون بهتره ( درست نکردن پست هایی بی اثر مثل این )
نازنین کریمی مقدم۱۹ دی ۱۳۹۹، ۱۷:۱۶
درود.
دوست عزیز تمامی مقالات ما حداقل یکبار توسط تیم ویراستاری هم بررسی میشوند و سعی میکنیم به صورت شفاف هر موضوعی رو آموزش بدیم.
سطح این مقاله پیشرفته هست و زمان نسبتا خوبی رو باید برای درک کدها بگذارید و خودتون یکبار به صورت عملی انجام بدید تا بهتر متوجه شید. اگر جایی از مقاله رو متوجه نشدید، ما در بخش کامنتها به سوالات تون جواب میدیم و اگر محتوایی باید به مقاله اضافه بشه، حتما اون رو ویرایش میکنیم.
ما همواره از نظرات شما استقبال میکنیم، اما انجام ندادن بعضی کارها از انجام دادنشون بهتره (قضاوت بیجا با ایمیل فیک مثل این :) )
راهنمای مقاله
dagger در اندروید چیست؟
فیلدها در Dagger
استفاده از Dagger در اندروید استودیو
مثالی از Dagger به همراه RxJava و Retrofit با معماری MVVM
جمع بندی
راهنما و فهرست مقاله
dagger در اندروید چیست؟
فیلدها در Dagger
استفاده از Dagger در اندروید استودیو
مثالی از Dagger به همراه RxJava و Retrofit با معماری MVVM