डिपेंडेंसी इंजेक्शन (DI) एक डिज़ाइन पैटर्न है जिसका उपयोग इनवर्जन ऑफ़ कंट्रोल (IoC) को लागू करने के लिए किया जाता है जहाँ निर्भरता बनाने और प्रबंधित करने का नियंत्रण एप्लिकेशन से बाहरी इकाई को स्थानांतरित किया जाता है। यह अधिक मॉड्यूलर, परीक्षण योग्य और रखरखाव योग्य कोड बनाने में मदद करता है। यह एक ऐसी तकनीक है जहाँ ऑब्जेक्ट बनाने की ज़िम्मेदारी कोड के अन्य भागों को हस्तांतरित की जाती है। यह ढीले युग्मन को बढ़ावा देता है, जिससे कोड अधिक मॉड्यूलर और प्रबंधित करने में आसान हो जाता है।
कक्षाओं को ठीक से काम करने के लिए अक्सर अन्य कक्षाओं के संदर्भों की आवश्यकता होती है। उदाहरण के लिए, एक Library
क्लास पर विचार करें जिसके लिए Book
क्लास की आवश्यकता होती है। इन आवश्यक कक्षाओं को निर्भरता के रूप में जाना जाता है। Library
क्लास को संचालित करने के लिए Book
क्लास के इंस्टेंस पर निर्भर रहना पड़ता है।
किसी क्लास के लिए आवश्यक ऑब्जेक्ट प्राप्त करने के तीन प्राथमिक तरीके हैं:
Library
क्लास Book
क्लास का अपना इंस्टेंस बनाएगा और आरंभ करेगा।Context
getters और getSystemService()
, इस तरह से काम करते हैं।Library
कंस्ट्रक्टर को पैरामीटर के रूप में Book
इंस्टेंस प्राप्त होगा।तीसरा विकल्प निर्भरता इंजेक्शन है! DI के साथ, आप क्लास की निर्भरताएँ प्रदान करते हैं बजाय इसके कि क्लास इंस्टेंस उन्हें स्वयं प्राप्त करे।
DI के बिना, एक Library
जो अपनी स्वयं की Book
निर्भरता बनाती है, वह इस तरह दिखाई दे सकती है:
class Library { private Book book = new Book(); void open() { book.read(); } } public class Main { public static void main(String[] args) { Library library = new Library(); library.open(); } }
यह DI का उदाहरण नहीं है क्योंकि Library
क्लास अपनी खुद की Book
बनाती है। यह समस्याग्रस्त हो सकता है क्योंकि:
Library
और Book
टाइट कपल्ड हैं। Library
का एक उदाहरण एक प्रकार की Book
का उपयोग करता है, जिससे उपवर्गों या वैकल्पिक कार्यान्वयन का उपयोग करना मुश्किल हो जाता है।Book
पर हार्ड निर्भरता परीक्षण को और अधिक चुनौतीपूर्ण बनाती है। Library
Book
के वास्तविक उदाहरण का उपयोग करती है, जिससे विभिन्न परीक्षण मामलों के लिए Book
को संशोधित करने के लिए परीक्षण डबल्स के उपयोग को रोका जा सकता है। DI के साथ, Library
के प्रत्येक इंस्टेंस द्वारा अपना स्वयं का Book
ऑब्जेक्ट निर्मित करने के स्थान पर, उसे अपने कन्स्ट्रक्टर में पैरामीटर के रूप में Book
ऑब्जेक्ट प्राप्त होता है:
class Library { private Book book; Library(Book book) { this.book = book; } void open() { book.read(); } } public class Main { public static void main(String[] args) { Book book = new Book(); Library library = new Library(book); library.open(); }
मुख्य फ़ंक्शन Library
उपयोग करता है। चूँकि Library
Book
पर निर्भर करती है, इसलिए ऐप Book
का एक इंस्टेंस बनाता है और फिर Library
का इंस्टेंस बनाने के लिए इसका उपयोग करता है। इस DI-आधारित दृष्टिकोण के लाभ ये हैं:
Library
की पुनः प्रयोज्यता : आप Book
के विभिन्न कार्यान्वयन को Library
में पास कर सकते हैं। उदाहरण के लिए, आप Book
का एक नया उपवर्ग परिभाषित कर सकते हैं जिसे EBook
कहा जाता है जिसे आप Library
द्वारा उपयोग करना चाहते हैं। DI के साथ, आप बस EBook
का एक उदाहरण Library
में पास करते हैं, और यह बिना किसी और बदलाव के काम करता है।Library
का आसान परीक्षण : आप विभिन्न परिदृश्यों का परीक्षण करने के लिए टेस्ट डबल्स में पास हो सकते हैं। एक परिदृश्य पर विचार करें जहां एक NotificationService
वर्ग एक Notification
वर्ग पर निर्भर करता है। DI के बिना, NotificationService
सीधे Notification
का एक उदाहरण बनाती है, जिससे विभिन्न प्रकार की सूचनाओं का उपयोग करना या विभिन्न अधिसूचना कार्यान्वयन के साथ सेवा का परीक्षण करना मुश्किल हो जाता है।
DI को स्पष्ट करने के लिए, आइए इस उदाहरण को पुनः दोहराते हैं:
interface Notification { void send(); } class EmailNotification implements Notification { @Override public void send() { // Send email notification } } class SMSNotification implements Notification { @Override public void send() { // Send SMS notification } } class NotificationService { void sendNotification(Notification notification) { notification.send(); } }
अब, NotificationService
किसी विशिष्ट क्लास के बजाय Notification
इंटरफ़ेस पर निर्भर करती है। यह Notification
के विभिन्न कार्यान्वयनों को परस्पर उपयोग करने की अनुमति देता है। आप sendNotification
विधि के माध्यम से वह कार्यान्वयन सेट कर सकते हैं जिसका आप उपयोग करना चाहते हैं:
NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification());
डीआई के तीन मुख्य प्रकार हैं:
class NotificationService { private final Notification notification; public NotificationService(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(new EmailNotification()); service.sendNotification(); } }
3. फ़ील्ड इंजेक्शन (या सेटर इंजेक्शन) : कुछ Android फ़्रेमवर्क क्लास, जैसे कि एक्टिविटी और फ़्रैगमेंट, सिस्टम द्वारा इंस्टेंटिएट किए जाते हैं, इसलिए कंस्ट्रक्टर इंजेक्शन संभव नहीं है। फ़ील्ड इंजेक्शन के साथ, क्लास बनने के बाद निर्भरताएँ इंस्टेंटिएट की जाती हैं।
class NotificationService { private Notification notification; public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(); service.setNotification(new EmailNotification()); service.sendNotification(); } }
4. विधि इंजेक्शन : निर्भरताएँ विधियों के माध्यम से प्रदान की जाती हैं, अक्सर @Inject
एनोटेशन का उपयोग करके।
पिछले उदाहरण में, आपने लाइब्रेरी का उपयोग किए बिना विभिन्न वर्गों की निर्भरताओं को मैन्युअल रूप से बनाया, प्रदान किया और प्रबंधित किया। इस दृष्टिकोण को मैन्युअल निर्भरता इंजेक्शन के रूप में जाना जाता है। जबकि यह सरल मामलों के लिए काम करता है, यह निर्भरता और वर्गों की संख्या बढ़ने पर बोझिल हो जाता है। मैन्युअल निर्भरता इंजेक्शन में कई कमियाँ हैं:
लाइब्रेरी आपके लिए निर्भरताएँ बनाकर और प्रदान करके इस प्रक्रिया को स्वचालित कर सकती हैं। ये लाइब्रेरी दो श्रेणियों में आती हैं:
डैगर जावा, कोटलिन और एंड्रॉइड के लिए एक लोकप्रिय निर्भरता इंजेक्शन लाइब्रेरी है, जिसे Google द्वारा बनाए रखा जाता है। डैगर आपके लिए निर्भरता ग्राफ बनाकर और प्रबंधित करके आपके ऐप में DI को सरल बनाता है। यह पूरी तरह से स्थिर, संकलन-समय निर्भरता प्रदान करता है, जो Guice जैसे प्रतिबिंब-आधारित समाधानों से जुड़े कई विकास और प्रदर्शन मुद्दों को संबोधित करता है।
ये फ्रेमवर्क रनटाइम पर निर्भरताओं को जोड़ते हैं:
ये फ्रेमवर्क संकलन समय पर निर्भरताओं को जोड़ने के लिए कोड उत्पन्न करते हैं:
निर्भरता इंजेक्शन का एक विकल्प सर्विस लोकेटर पैटर्न है। यह डिज़ाइन पैटर्न कक्षाओं को उनकी ठोस निर्भरताओं से अलग करने में भी मदद करता है। आप सर्विस लोकेटर नामक एक क्लास बनाते हैं जो निर्भरताएँ बनाता और संग्रहीत करता है, उन्हें मांग पर प्रदान करता है।
object ServiceLocator { fun getProcessor(): Processor = Processor() } class Computer { private val processor = ServiceLocator.getProcessor() fun start() { processor.run() } } fun main(args: Array<String>) { val computer = Computer() computer.start() }
सर्विस लोकेटर पैटर्न निर्भरता इंजेक्शन से इस मामले में अलग है कि निर्भरता का उपभोग कैसे किया जाता है। सर्विस लोकेटर पैटर्न के साथ, क्लास अपनी ज़रूरत के अनुसार निर्भरता का अनुरोध करते हैं; निर्भरता इंजेक्शन के साथ, ऐप सक्रिय रूप से आवश्यक ऑब्जेक्ट प्रदान करता है।
डैगर 2 एंड्रॉयड के लिए एक लोकप्रिय DI फ्रेमवर्क है। यह संकलन-समय कोड जनरेशन का उपयोग करता है और अपने उच्च प्रदर्शन के लिए जाना जाता है। डैगर 2 निर्भरता को संभालने के लिए आवश्यक कोड उत्पन्न करके निर्भरता इंजेक्शन की प्रक्रिया को सरल बनाता है, बॉयलरप्लेट को कम करता है और दक्षता में सुधार करता है।
डैगर 2 एंड्रॉयड में निर्भरता इंजेक्शन के लिए एक एनोटेशन-आधारित लाइब्रेरी है। यहाँ मुख्य एनोटेशन और उनके उद्देश्य दिए गए हैं:
ApiClient
प्रदान कर सकता है।@Module
और @Inject
जोड़ता है। इसमें सभी मॉड्यूल शामिल हैं और यह एप्लिकेशन के लिए बिल्डर प्रदान करता है।@Provides
के समान लेकिन अधिक संक्षिप्त। डैगर आपके प्रोजेक्ट के लिए एक निर्भरता ग्राफ उत्पन्न कर सकता है, जिससे यह निर्धारित कर सकता है कि आवश्यकता पड़ने पर निर्भरता कहाँ से प्राप्त की जाए। इसे सक्षम करने के लिए, आपको एक इंटरफ़ेस बनाना होगा और इसे @Component
के साथ एनोटेट करना होगा।
@Component
इंटरफ़ेस के भीतर, आप उन विधियों को परिभाषित करते हैं जो आपके लिए आवश्यक कक्षाओं के उदाहरण लौटाते हैं (उदाहरण के लिए, BookRepository
)। @Component
एनोटेशन Dagger को एक कंटेनर बनाने का निर्देश देता है जिसमें वह सभी निर्भरताएँ हों जो उसके द्वारा प्रदर्शित प्रकारों को संतुष्ट करने के लिए आवश्यक हों। इस कंटेनर को Dagger घटक के रूप में जाना जाता है, और इसमें उन वस्तुओं का एक ग्राफ़ होता है जिन्हें Dagger उनकी निर्भरताओं के साथ प्रदान करना जानता है।
आइये LibraryRepository
से संबंधित एक उदाहरण पर विचार करें:
LibraryRepository
कंस्ट्रक्टर में @Inject
एनोटेशन जोड़ें ताकि Dagger को पता चले कि LibraryRepository
का इंस्टेंस कैसे बनाया जाए। public class LibraryRepository { private final LocalLibraryDataSource localDataSource; private final RemoteLibraryDataSource remoteDataSource; @Inject public LibraryRepository(LocalLibraryDataSource localDataSource, RemoteLibraryDataSource remoteDataSource) { this.localDataSource = localDataSource; this.remoteDataSource = remoteDataSource; } }
2. निर्भरताओं को एनोटेट करें : इसी तरह, निर्भरताओं ( LocalLibraryDataSource
और RemoteLibraryDataSource
) के कंस्ट्रक्टर्स को एनोटेट करें ताकि डैगर को पता चले कि उन्हें कैसे बनाया जाए।
public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } }
3. घटक को परिभाषित करें : निर्भरता ग्राफ को परिभाषित करने के लिए @Component
के साथ एनोटेट किया गया एक इंटरफ़ेस बनाएं।
@Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); }
जब आप प्रोजेक्ट बनाते हैं, तो Dagger आपके लिए ApplicationComponent
इंटरफ़ेस का कार्यान्वयन उत्पन्न करता है, जिसे आमतौर पर DaggerApplicationComponent
नाम दिया जाता है।
अब आप उत्पन्न घटक का उपयोग अपने वर्गों के उदाहरण प्राप्त करने के लिए कर सकते हैं, जिनकी निर्भरताएं स्वचालित रूप से इंजेक्ट की जाती हैं:
public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }
अपनी गतिविधि या खंड में, आप LibraryRepository
इंस्टेंस को पुनः प्राप्त कर सकते हैं:
public class MainActivity extends AppCompatActivity { @Inject LibraryRepository libraryRepository; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MainApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected libraryRepository } }
1. मॉड्यूल
∘ मॉड्यूल की मुख्य अवधारणाएँ
∘ घटकों में मॉड्यूल शामिल करना
2. कार्यक्षेत्र
3. घटक
4. घटक निर्भरताएँ
5. रनटाइम बाइंडिंग
डैगर 2 में मॉड्यूल @Module
के साथ एनोटेट किए गए क्लास हैं जो घटकों को निर्भरता प्रदान करते हैं। उनमें @Provides
या @Binds
के साथ एनोटेट किए गए तरीके होते हैं जो यह निर्दिष्ट करते हैं कि निर्भरता कैसे बनाई और आपूर्ति की जाए। मॉड्यूल आपके एप्लिकेशन की ज़रूरत वाले ऑब्जेक्ट के निर्माण को व्यवस्थित और प्रबंधित करने के लिए आवश्यक हैं।
@Provides
से अधिक संक्षिप्त है और इसका उपयोग तब किया जाता है जब मॉड्यूल एक अमूर्त वर्ग होता है।मॉड्यूल का उदाहरण
@Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } @Provides @Singleton OkHttpClient provideOkHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); } }
इस उदाहरण में, NetworkModule
@Module
के साथ एनोटेट किया गया एक क्लास है। इसमें @Provides
के साथ एनोटेट किए गए दो तरीके हैं जो Retrofit
और OkHttpClient
के इंस्टेंस बनाते हैं और लौटाते हैं।
@Binds का उपयोग करना
जब आपके पास एक इंटरफ़ेस और उसका कार्यान्वयन होता है, तो आप कार्यान्वयन को इंटरफ़ेस से जोड़ने के लिए @Binds
उपयोग कर सकते हैं। यह @Provides
उपयोग करने से ज़्यादा संक्षिप्त है।
public interface ApiService { void fetchData(); } public class ApiServiceImpl implements ApiService { @Override public void fetchData() { // Implementation } } @Module public abstract class ApiModule { @Binds abstract ApiService bindApiService(ApiServiceImpl apiServiceImpl); }
इस उदाहरण में, ApiModule
@Module
के साथ एनोटेट किया गया एक अमूर्त वर्ग है। ApiServiceImpl
ApiService
से बाँधने के लिए bindApiService
विधि को @Binds
के साथ एनोटेट किया गया है।
मॉड्यूल को उनके द्वारा प्रदान की जाने वाली कार्यक्षमता के आधार पर व्यवस्थित किया जा सकता है। उदाहरण के लिए, आपके पास नेटवर्क संचालन, डेटाबेस संचालन और UI-संबंधित निर्भरताओं के लिए अलग-अलग मॉड्यूल हो सकते हैं।
उदाहरण:
Retrofit
और OkHttpClient
जैसी नेटवर्क-संबंधित निर्भरताएं प्रदान करता है।RoomDatabase
जैसी डेटाबेस-संबंधित निर्भरताएं प्रदान करता है.ViewModel
और Presenter
जैसी UI-संबंधित निर्भरताएं प्रदान करता है।मॉड्यूल को घटकों में शामिल किया जाता है ताकि उन वर्गों को निर्भरता प्रदान की जा सके जिन्हें उनकी आवश्यकता है। यहां बताया गया है कि आप इसे कैसे सेट कर सकते हैं:
एप्लीकेशन घटक.जावा:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
इस उदाहरण में, ApplicationComponent
में अनुप्रयोग को निर्भरता प्रदान करने के लिए NetworkModule
और DatabaseModule
शामिल हैं।
डैगर 2 में स्कोप एनोटेशन हैं जो निर्भरता के जीवनचक्र को परिभाषित करते हैं। वे सुनिश्चित करते हैं कि निर्भरता का एक ही उदाहरण बनाया गया है और निर्दिष्ट दायरे में साझा किया गया है। यह मेमोरी को कुशलतापूर्वक प्रबंधित करने और यह सुनिश्चित करने में मदद करता है कि निर्भरता का पुन: उपयोग उचित रूप से किया जाए।
1. सिंगलटन स्कोप
परिभाषा : @Singleton
स्कोप यह सुनिश्चित करता है कि निर्भरता का एक एकल उदाहरण बनाया जाए और पूरे अनुप्रयोग के जीवनचक्र में साझा किया जाए।
इस क्षेत्र का उपयोग आम तौर पर उन निर्भरताओं के लिए किया जाता है जिन्हें पूरे अनुप्रयोग में साझा करने की आवश्यकता होती है, जैसे नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस या साझा प्राथमिकताएं।
उदाहरण:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
इस उदाहरण में, @Singleton
एनोटेशन यह सुनिश्चित करता है कि NetworkModule
और DatabaseModule
द्वारा प्रदान किए गए Retrofit
और Database
इंस्टेंस सिंगलटन हैं और पूरे अनुप्रयोग में साझा किए गए हैं।
2. गतिविधि का दायरा
परिभाषा : @ActivityScope
(एक कस्टम स्कोप) यह सुनिश्चित करता है कि किसी गतिविधि के जीवनचक्र के भीतर निर्भरता का एक एकल उदाहरण बनाया और साझा किया जाए।
यह दायरा उन निर्भरताओं के लिए उपयोगी है जो किसी गतिविधि के लिए विशिष्ट हैं और जिन्हें प्रत्येक बार गतिविधि को पुनः बनाए जाने पर पुनः बनाया जाना चाहिए, जैसे प्रस्तुतकर्ता या दृश्य मॉडल।
उदाहरण :
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
इस उदाहरण में, @ActivityScope
एनोटेशन यह सुनिश्चित करता है कि ActivityModule
द्वारा प्रदान की गई निर्भरताएं गतिविधि के जीवनचक्र तक सीमित हैं।
3. खंड क्षेत्र
परिभाषा : @FragmentScope
(एक अन्य कस्टम स्कोप) यह सुनिश्चित करता है कि निर्भरता का एक एकल उदाहरण फ्रेगमेंट के जीवनचक्र के भीतर बनाया और साझा किया जाए।
उपयोग मामला: यह दायरा उन निर्भरताओं के लिए उपयोगी है जो किसी फ्रेगमेंट के लिए विशिष्ट हैं और फ्रेगमेंट के पुनः निर्माण के समय हर बार उन्हें पुनः निर्मित किया जाना चाहिए, जैसे कि फ्रेगमेंट-विशिष्ट प्रस्तुतकर्ता या दृश्य मॉडल।
उदाहरण :
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); }
इस उदाहरण में, @FragmentScope
एनोटेशन यह सुनिश्चित करता है कि FragmentModule
द्वारा प्रदान की गई निर्भरताएं फ्रेगमेंट के जीवनचक्र तक सीमित हैं।
घटक निर्भरताएँ एक घटक को दूसरे पर निर्भर रहने देती हैं, जिससे निर्भरताओं का पुनः उपयोग संभव हो जाता है। घटक निर्भरता के दो मुख्य प्रकार हैं:
1. अनुप्रयोग घटक
परिभाषा : एप्लिकेशन घटक निर्भरताएँ प्रदान करता है जो पूरे एप्लिकेशन में आवश्यक हैं। यह आमतौर पर @Singleton
के साथ स्कोप किया जाता है ताकि यह सुनिश्चित किया जा सके कि निर्भरताएँ पूरे एप्लिकेशन में साझा की जाती हैं।
इस घटक का उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें वैश्विक रूप से उपलब्ध होना आवश्यक है, जैसे नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस या साझा प्राथमिकताएं।
उदाहरण :
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
इस उदाहरण में, ApplicationComponent
Retrofit
और Database
इंस्टैंस प्रदान करने के लिए जिम्मेदार है, जिन्हें पूरे एप्लिकेशन में साझा किया जाता है।
2. गतिविधि घटक
परिभाषा : गतिविधि घटक ऐसी निर्भरताएँ प्रदान करता है जो किसी विशिष्ट गतिविधि के भीतर आवश्यक होती हैं। इसे आमतौर पर एक कस्टम स्कोप के साथ स्कोप किया जाता है, जैसे कि @ActivityScope
, यह सुनिश्चित करने के लिए कि हर बार गतिविधि को फिर से बनाए जाने पर निर्भरताएँ फिर से बनाई जाएँ।
इस घटक का उपयोग उन निर्भरताओं के लिए किया जाता है जो किसी गतिविधि के लिए विशिष्ट होती हैं, जैसे प्रस्तुतकर्ता या दृश्य मॉडल।
उदाहरण :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
इस उदाहरण में, ActivityComponent
, ApplicationComponent
पर निर्भर करता है और MainActivity
के लिए विशिष्ट निर्भरताएँ प्रदान करता है।
घटक निर्भरताएँ एक घटक को दूसरे पर निर्भर रहने देती हैं, जिससे निर्भरताओं का पुनः उपयोग संभव हो जाता है। घटक निर्भरता के दो मुख्य प्रकार हैं:
1. उपघटक:
एक उपघटक दूसरे घटक का बच्चा होता है और अपने पैरेंट की निर्भरताओं तक पहुँच सकता है। उपघटक पैरेंट घटक के भीतर परिभाषित किए जाते हैं और इसके दायरे को विरासत में ले सकते हैं।
उदाहरण :
@ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); }
इस उदाहरण में, ActivitySubcomponent
मूल घटक का उपघटक है और इसकी निर्भरताओं तक पहुँच सकता है।
2. निर्भरता विशेषता
यह किसी घटक को उपघटक बने बिना किसी अन्य घटक पर निर्भर रहने की अनुमति देता है। आश्रित घटक मूल घटक द्वारा प्रदान की गई निर्भरताओं तक पहुँच सकता है।
उदाहरण :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
इस उदाहरण में, ActivityComponent
, ApplicationComponent
पर निर्भर करता है और इसकी निर्भरताओं तक पहुँच सकता है।
डैगर 2 में रनटाइम बाइंडिंग निर्भरताओं के प्रावधान को संदर्भित करता है, जो उस संदर्भ के आधार पर रनटाइम पर बनाए और प्रबंधित किए जाते हैं जिसमें उनकी आवश्यकता होती है।
1. आवेदन संदर्भ
परिभाषा : एप्लिकेशन संदर्भ एक ऐसा संदर्भ है जो पूरे एप्लिकेशन के जीवनचक्र से जुड़ा होता है। इसका उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें एप्लिकेशन के जितना ही समय तक जीवित रहना होता है।
निर्भरताएँ जो पूरे एप्लिकेशन में साझा की जाती हैं और प्रत्येक गतिविधि या खंड के लिए उन्हें फिर से बनाने की आवश्यकता नहीं होती है। उदाहरणों में नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस और साझा प्राथमिकताएँ शामिल हैं।
उदाहरण :
@Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application provideApplication() { return application; } @Provides @Singleton Context provideApplicationContext() { return application.getApplicationContext(); } }
इस उदाहरण में, AppModule
एक सिंगलटन निर्भरता के रूप में एप्लिकेशन संदर्भ प्रदान करता है। provideApplicationContext
विधि यह सुनिश्चित करती है कि प्रदान किया गया संदर्भ एप्लिकेशन के जीवनचक्र से जुड़ा हुआ है।
2. गतिविधि संदर्भ
परिभाषा : गतिविधि संदर्भ एक संदर्भ है जो किसी विशिष्ट गतिविधि के जीवनचक्र से जुड़ा होता है। इसका उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें गतिविधि के समान ही लंबे समय तक जीवित रहने की आवश्यकता होती है।
निर्भरताएँ जो किसी गतिविधि के लिए विशिष्ट होती हैं और हर बार गतिविधि को फिर से बनाए जाने पर उन्हें फिर से बनाया जाना चाहिए। उदाहरणों में व्यू मॉडल, प्रेजेंटर और UI-संबंधित निर्भरताएँ शामिल हैं।
उदाहरण :
@Module public class ActivityModule { private final Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } @Provides @ActivityScope Activity provideActivity() { return activity; } @Provides @ActivityScope Context provideActivityContext() { return activity; } }
इस उदाहरण में, ActivityModule
गतिविधि संदर्भ को एक स्कोप्ड निर्भरता के रूप में प्रदान करता है। provideActivityContext
विधि यह सुनिश्चित करती है कि प्रदान किया गया संदर्भ गतिविधि के जीवनचक्र से जुड़ा हुआ है।
इन रनटाइम बाइंडिंग का उपयोग करने के लिए, आपको अपने घटकों में संबंधित मॉड्यूल शामिल करने होंगे:
आवेदन घटक :
@Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); }
गतिविधि घटक :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); }
सन्दर्भों को शामिल करना
एक बार जब आप अपने घटकों और मॉड्यूलों को सेट कर लेते हैं, तो आप आवश्यकतानुसार अपने वर्गों में संदर्भों को सम्मिलित कर सकते हैं।
एक गतिविधि में उदाहरण :
public class MainActivity extends AppCompatActivity { @Inject Context activityContext; @Inject Context applicationContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ApplicationComponent appComponent = ((MyApplication) getApplication()).getApplicationComponent(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .applicationComponent(appComponent) .activityModule(new ActivityModule(this)) .build(); activityComponent.inject(this); // Use the injected contexts Log.d("MainActivity", "Activity Context: " + activityContext); Log.d("MainActivity", "Application Context: " + applicationContext); } }
इस उदाहरण में, MainActivity
निर्भरता इंजेक्शन के माध्यम से गतिविधि संदर्भ और अनुप्रयोग संदर्भ दोनों प्राप्त करता है। यह गतिविधि को निर्भरताओं की विशिष्ट आवश्यकताओं के आधार पर उपयुक्त संदर्भ का उपयोग करने की अनुमति देता है।
अपने प्रोजेक्ट में Dagger 2 का उपयोग करने के लिए, आपको अपनी build.gradle
फ़ाइल में निम्नलिखित निर्भरताएँ जोड़नी होंगी:
dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }
2.x
Dagger 2 के नवीनतम संस्करण से प्रतिस्थापित करें।
निर्भरता प्रदान करने के लिए एक मॉड्यूल बनाएँ। उदाहरण के लिए, एक Retrofit
इंस्टेंस प्रदान करने के लिए एक NetworkModule
:
@Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } }
मॉड्यूल और उन कक्षाओं को जोड़ने के लिए एक घटक बनाएं जिन्हें निर्भरता की आवश्यकता है:
@Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
अपने क्लास में निर्भरता को इंजेक्ट करने के लिए घटक का उपयोग करें। उदाहरण के लिए, अपने Application
क्लास में:
public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .networkModule(new NetworkModule()) .build(); applicationComponent.inject(this); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }
अब आप अपनी कक्षाओं में इंजेक्ट की गई निर्भरता का उपयोग कर सकते हैं। उदाहरण के लिए, किसी Activity
में:
public class MainActivity extends AppCompatActivity { @Inject Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected Retrofit instance // ... } }
आइये इस विषय को संक्षेप में प्रस्तुत करें: