मल्टी-मॉड्यूल ऐप्लिकेशन में डैगर का इस्तेमाल करना

एक से ज़्यादा Gradle मॉड्यूल वाले प्रोजेक्ट को मल्टी-मॉड्यूल प्रोजेक्ट कहा जाता है. यह ऐसे मल्टी-मॉड्यूल प्रोजेक्ट में है जो बिना किसी सुविधा के एक APK के तौर पर भेजा जाता है मॉड्यूल का इस्तेमाल करते हैं, तो आम तौर पर एक app मॉड्यूल होता है, जो ज़्यादातर मॉड्यूल पर निर्भर करता है मॉड्यूल और base या core मॉड्यूल को इकट्ठा करना बाकी है, मॉड्यूल आम तौर पर इन पर निर्भर करते हैं. app मॉड्यूल में आम तौर पर, आपके Application क्लास, जबकि base मॉड्यूल में आपके प्रोजेक्ट के सभी मॉड्यूल में शेयर की जाने वाली सभी सामान्य क्लास शामिल होती हैं.

ऐप्लिकेशन कॉम्पोनेंट के बारे में बताने के लिए, app मॉड्यूल अच्छी जगह है (इसके लिए उदाहरण के लिए, नीचे दी गई इमेज में ApplicationComponent) से ऑब्जेक्ट मिल सकते हैं दूसरे कॉम्पोनेंट के साथ-साथ आपके ऐप्लिकेशन के सिंगलटन की भी ज़रूरत पड़ सकती है. इस तौर पर उदाहरण के लिए, OkHttpClient, JSON पार्सर, आपके डेटाबेस के लिए ऐक्सेसर, या SharedPreferences ऑब्जेक्ट जिन्हें core मॉड्यूल में तय किया जा सकता है, app मॉड्यूल में बताए गए ApplicationComponent से मिलेगी.

app मॉड्यूल में, आपके पास ऐसे दूसरे कॉम्पोनेंट भी हो सकते हैं जो कम समय तक चलते हैं. इसका एक उदाहरण उपयोगकर्ता के खास कॉन्फ़िगरेशन वाला UserComponent हो सकता है (जैसे कि UserSession) को लॉग इन करने के बाद.

अपने प्रोजेक्ट के अलग-अलग मॉड्यूल में, कम से कम एक मॉड्यूल तय किया जा सकता है वह सबकॉम्पोनेंट जिसमें उस मॉड्यूल के लिए खास लॉजिक होता है, जैसा कि पहली इमेज में दिखाया गया है.

पहला डायग्राम. Google Analytics 4 में डैगर ग्राफ़ का उदाहरण मल्टी-मॉड्यूल प्रोजेक्ट

उदाहरण के लिए, login मॉड्यूल में, आपके पास LoginComponent हो सकता है के दायरे में, पसंद के मुताबिक @ModuleScope के एनोटेशन हैं, जो समान ऑब्जेक्ट मुहैया करा सकते हैं उस सुविधा से जुड़ जाना चाहिए, जैसे कि LoginRepository. उस मॉड्यूल के अंदर, ये काम भी किए जा सकते हैं ऐसे दूसरे कॉम्पोनेंट होते हैं जो एक अलग कस्टम वाले LoginComponent पर निर्भर होते हैं स्कोप, उदाहरण के लिए @FeatureScope के लिए LoginActivityComponent या a TermsAndConditionsComponent जहां ज़्यादा सुविधा वाले लॉजिक का इस्तेमाल किया जा सकता है जैसे कि ViewModel ऑब्जेक्ट.

Registration जैसे दूसरे मॉड्यूल के लिए, आपके पास मिलता-जुलता सेटअप होगा.

मल्टी-मॉड्यूल प्रोजेक्ट के लिए एक सामान्य नियम यह है कि एक जैसे लेवल के मॉड्यूल एक-दूसरे पर निर्भर नहीं होना चाहिए. अगर वे सहमत हैं, तो देखें कि क्या (उनके बीच की डिपेंडेंसी), पैरंट मॉड्यूल का हिस्सा होनी चाहिए. अगर ऐसा है, तो क्लास को पैरंट मॉड्यूल में ले जाने के लिए रीफ़ैक्टर करें; अगर नहीं है, तो नया मॉड्यूल बनाएं जो पैरंट मॉड्यूल को बड़ा करता है और दोनों ओरिजनल मॉड्यूल में नया मॉड्यूल.

सबसे सही तरीका यह है कि आम तौर पर, मॉड्यूल को नीचे दिए गए मामलों में देखें:

  • आपको फ़ील्ड इंजेक्शन डालना होगा, जैसा कि LoginActivityComponent के साथ किया जाता है.

  • आपको ऑब्जेक्ट का स्कोप करना होगा, जैसा LoginComponent के साथ किया जाता है.

अगर इनमें से कोई भी वजह लागू नहीं होती है और आपको डैगर को यह बताना है कि उस मॉड्यूल से ऑब्जेक्ट इकट्ठा करने के लिए, @Provides की मदद से डैगर मॉड्यूल बनाएं और उसे सार्वजनिक करें या @Binds तरीकों से, अगर इन क्लास में कंस्ट्रक्शन इंजेक्शन नहीं बनाया जा सकता.

डैगर सबकॉम्पोनेंट के साथ लागू करना

Android ऐप्लिकेशन में डैगर का इस्तेमाल करना दस्तावेज़ पेज पर, दस्तावेज़ बनाने और उन्हें इस्तेमाल करने का तरीका बताया गया है सबकॉम्पोनेंट. हालांकि, आप उसी कोड का उपयोग नहीं कर सकते, क्योंकि सुविधा वाले मॉड्यूल को app मॉड्यूल के बारे में जानकारी नहीं है. उदाहरण के लिए, अगर आपको लगता है कि के सामान्य लॉगिन प्रवाह और पिछले पेज में हमारे कोड के बारे में है, तो यह और ज़्यादा कंपाइल करें:

Kotlin

class LoginActivity: Activity() {
  ...

  override fun onCreate(savedInstanceState: Bundle?) {
    // Creation of the login graph using the application graph
    loginComponent = (applicationContext as MyDaggerApplication)
                        .appComponent.loginComponent().create()

    // Make Dagger instantiate @Inject fields in LoginActivity
    loginComponent.inject(this)
    ...
  }
}

Java

public class LoginActivity extends Activity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Creation of the login graph using the application graph
        loginComponent = ((MyApplication) getApplicationContext())
                                .appComponent.loginComponent().create();

        // Make Dagger instantiate @Inject fields in LoginActivity
        loginComponent.inject(this);

        ...
    }
}

इसकी वजह यह है कि login मॉड्यूल को MyApplication के बारे में जानकारी नहीं है और न ही appComponent. इसके काम करने के लिए, आपको इस सुविधा में एक इंटरफ़ेस तय करना होगा मॉड्यूल जो MyApplication की ज़रूरत के मुताबिक FeatureComponent देता है .

नीचे दिए गए उदाहरण में, LoginComponentProvider इंटरफ़ेस को तय किया जा सकता है जो लॉगिन फ़्लो के लिए login मॉड्यूल में LoginComponent उपलब्ध कराता है:

Kotlin

interface LoginComponentProvider {
    fun provideLoginComponent(): LoginComponent
}

Java

public interface LoginComponentProvider {
   public LoginComponent provideLoginComponent();
}

अब LoginActivity, कोड के स्निपेट के बजाय उस इंटरफ़ेस का इस्तेमाल करेगा ऊपर परिभाषित किया गया है:

Kotlin

class LoginActivity: Activity() {
  ...

  override fun onCreate(savedInstanceState: Bundle?) {
    loginComponent = (applicationContext as LoginComponentProvider)
                        .provideLoginComponent()

    loginComponent.inject(this)
    ...
  }
}

Java

public class LoginActivity extends Activity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        loginComponent = ((LoginComponentProvider) getApplicationContext())
                                .provideLoginComponent();

        loginComponent.inject(this);

        ...
    }
}

अब MyApplication को वह इंटरफ़ेस लागू करना होगा ज़रूरी तरीके:

Kotlin

class MyApplication: Application(), LoginComponentProvider {
  // Reference to the application graph that is used across the whole app
  val appComponent = DaggerApplicationComponent.create()

  override fun provideLoginComponent(): LoginComponent {
    return appComponent.loginComponent().create()
  }
}

Java

public class MyApplication extends Application implements LoginComponentProvider {
  // Reference to the application graph that is used across the whole app
  ApplicationComponent appComponent = DaggerApplicationComponent.create();

  @Override
  public LoginComponent provideLoginComponent() {
    return appComponent.loginComponent.create();
  }
}

इस तरह, मल्टी-मॉड्यूल प्रोजेक्ट में डैगर सबकॉम्पोनेंट का इस्तेमाल इस तरह किया जा सकता है. सुविधा मॉड्यूल होने पर, सलूशन अलग-अलग होता है. इसकी वजह यह है कि मॉड्यूल एक-दूसरे पर निर्भर करते हैं.

फ़ीचर मॉड्यूल के साथ कॉम्पोनेंट डिपेंडेंसी

सुविधा वाले मॉड्यूल में, मॉड्यूल के काम करने का तरीका एक-दूसरे को इन्वर्टेड करें. सुविधा वाले app मॉड्यूल के बजाय मॉड्यूल हैं, तो सुविधा वाले मॉड्यूल app मॉड्यूल पर निर्भर करते हैं. दूसरी इमेज देखें देखें कि मॉड्यूल कैसे स्ट्रक्चर किए जाते हैं.

दूसरा डायग्राम. Google Analytics 4 में डैगर ग्राफ़ का उदाहरण फ़ीचर मॉड्यूल वाला प्रोजेक्ट

डैगर में, कॉम्पोनेंट को अपने सबकॉम्पोनेंट के बारे में जानना ज़रूरी है. यह जानकारी को पैरंट कॉम्पोनेंट में जोड़े गए डैगर मॉड्यूल में शामिल किया गया है (जैसे, Android ऐप्लिकेशन में डैगर का इस्तेमाल करना में SubcomponentsModule मॉड्यूल).

माफ़ करें, ऐप्लिकेशन और फ़ीचर मॉड्यूल में, app मॉड्यूल से सबकॉम्पोनेंट नहीं दिखता है, क्योंकि यह बिल्ड पाथ में नहीं है. उदाहरण के लिए, a LoginComponent को login फ़ीचर मॉड्यूल, app मॉड्यूल में ApplicationComponent तय किया गया है.

डैगर में कॉम्पोनेंट डिपेंडेंसी नाम का एक तरीका है, जिसका इस्तेमाल करके इस समस्या को हल करें. चाइल्ड कॉम्पोनेंट, चाइल्ड कॉम्पोनेंट, पैरंट कॉम्पोनेंट पर निर्भर करता है. के साथ माता-पिता और बच्चे के बीच कोई संबंध न हो; अब कॉम्पोनेंट, दूसरों पर निर्भर करते हैं कुछ डिपेंडेंसी पाने के लिए. कॉम्पोनेंट को ग्राफ़ में दिए गए टाइप दिखाने चाहिए इनका इस्तेमाल किया जा सकता है.

उदाहरण के लिए: login नाम का एक फ़ीचर मॉड्यूल बनाना चाहता है LoginComponent, जो इसमें उपलब्ध AppComponent पर निर्भर करता है app Gradle मॉड्यूल.

यहां उन क्लास और AppComponent की परिभाषाएं दी गई हैं जो इनमें शामिल हैं app Gradle मॉड्यूल:

Kotlin

// UserRepository's dependencies
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }

// UserRepository is scoped to AppComponent
@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

@Singleton
@Component
interface AppComponent { ... }

Java

// UserRepository's dependencies
public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {}
}

public class UserRemoteDataSource {

    @Inject
    public UserRemoteDataSource() { }
}

// UserRepository is scoped to AppComponent
@Singleton
public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}

@Singleton
@Component
public interface ApplicationComponent { ... }

आपके login Gradle मॉड्यूल में, जिसमें app Gradle मॉड्यूल शामिल है, आपके पास LoginActivity जिसे इंजेक्ट करने के लिए LoginViewModel इंस्टेंस की ज़रूरत होती है:

Kotlin

// LoginViewModel depends on UserRepository that is scoped to AppComponent
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

Java

// LoginViewModel depends on UserRepository that is scoped to AppComponent
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

LoginViewModel की, UserRepository पर डिपेंडेंसी है जो उपलब्ध है और दायरा AppComponent है. चलिए, एक ऐसा LoginComponent बनाते हैं जो इन चीज़ों पर निर्भर करता है LoginActivity इंजेक्ट करने के लिए AppComponent:

Kotlin

// Use the dependencies attribute in the Component annotation to specify the
// dependencies of this Component
@Component(dependencies = [AppComponent::class])
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

Java

// Use the dependencies attribute in the Component annotation to specify the
// dependencies of this Component
@Component(dependencies = AppComponent.class)
public interface LoginComponent {

    void inject(LoginActivity loginActivity);
}

LoginComponent, AppComponent को डिपेंडेंसी के बारे में बताता है. इसके लिए, इसे कॉम्पोनेंट के एनोटेशन का डिपेंडेंसी पैरामीटर. क्योंकि LoginActivity यह कर पाएगा डैगर से डाला जाएगा, तो इंटरफ़ेस में inject() तरीका जोड़ें.

LoginComponent बनाते समय, AppComponent का एक इंस्टेंस ऐसा होना चाहिए पास हुआ. इसके लिए, कॉम्पोनेंट फ़ैक्ट्री का इस्तेमाल करें:

Kotlin

@Component(dependencies = [AppComponent::class])
interface LoginComponent {

    @Component.Factory
    interface Factory {
        // Takes an instance of AppComponent when creating
        // an instance of LoginComponent
        fun create(appComponent: AppComponent): LoginComponent
    }

    fun inject(activity: LoginActivity)
}

Java

@Component(dependencies = AppComponent.class)
public interface LoginComponent {

    @Component.Factory
    interface Factory {
        // Takes an instance of AppComponent when creating
        // an instance of LoginComponent
        LoginComponent create(AppComponent appComponent);
    }

    void inject(LoginActivity loginActivity);
}

अब LoginActivity, LoginComponent का एक इंस्टेंस बना सकता है और inject() तरीका.

Kotlin

class LoginActivity: Activity() {

    // You want Dagger to provide an instance of LoginViewModel from the Login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Gets appComponent from MyApplication available in the base Gradle module
        val appComponent = (applicationContext as MyApplication).appComponent

        // Creates a new instance of LoginComponent
        // Injects the component to populate the @Inject fields
        DaggerLoginComponent.factory().create(appComponent).inject(this)

        super.onCreate(savedInstanceState)

        // Now you can access loginViewModel
    }
}

Java

public class LoginActivity extends Activity {

    // You want Dagger to provide an instance of LoginViewModel from the Login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Gets appComponent from MyApplication available in the base Gradle module
        AppComponent appComponent = ((MyApplication) getApplicationContext()).appComponent;

        // Creates a new instance of LoginComponent
        // Injects the component to populate the @Inject fields
        DaggerLoginComponent.factory().create(appComponent).inject(this);

        // Now you can access loginViewModel
    }
}

LoginViewModel, UserRepository पर निर्भर है; और LoginComponent होने के लिए इसे AppComponent से ऐक्सेस कर सकता है, AppComponent को इसे बिना अनुमति के सार्वजनिक करना होगा इसका इंटरफ़ेस:

Kotlin

@Singleton
@Component
interface AppComponent {
    fun userRepository(): UserRepository
}

Java

@Singleton
@Component
public interface AppComponent {
    UserRepository userRepository();
}

डिपेंडेंट कॉम्पोनेंट वाले स्कोपिंग नियम, इनके साथ की तरह ही काम करते हैं सबकॉम्पोनेंट. LoginComponent, AppComponent के इंस्टेंस का इस्तेमाल करता है, इसलिए वे एक जैसे स्कोप वाली जानकारी का इस्तेमाल नहीं कर सकते.

अगर आपको LoginViewModel को LoginComponent में शामिल करना है, तो ऐसा करें आपने पहले कस्टम @ActivityScope एनोटेशन का इस्तेमाल किया था.

Kotlin

@ActivityScope
@Component(dependencies = [AppComponent::class])
interface LoginComponent { ... }

@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

Java

@ActivityScope
@Component(dependencies = AppComponent.class)
public interface LoginComponent { ... }

@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

सबसे सही तरीके

  • ApplicationComponent हमेशा app मॉड्यूल में होना चाहिए.

  • अगर आपको फ़ील्ड इंजेक्शन डालना है, तो मॉड्यूल में डैगर कॉम्पोनेंट बनाएं नहीं डाला है या आपको एक खास फ़्लो के लिए ऑब्जेक्ट का स्कोप करने की ज़रूरत है आपका ऐप्लिकेशन.

  • Gradle मॉड्यूल, जो यूटिलिटी या हेल्पर के तौर पर होते हैं और जिनकी ज़रूरत नहीं होती का इस्तेमाल करके ग्राफ़ बनाना है (इसलिए आपको डैगर कॉम्पोनेंट की ज़रूरत होगी), उन क्लास के @provided और @Binds तरीकों वाले सार्वजनिक डैगर मॉड्यूल कंस्ट्रक्टर इंजेक्शन का समर्थन नहीं करते हैं.

  • सुविधा वाले मॉड्यूल के साथ Android ऐप्लिकेशन में डैगर का इस्तेमाल करने के लिए, कॉम्पोनेंट का इस्तेमाल करें आपके पास उपलब्ध डिपेंडेंसी को ऐक्सेस करने के लिए डिपेंडेंसी ApplicationComponent को app मॉड्यूल में तय किया गया है.