মাল্টি-মডিউল অ্যাপে ড্যাগার ব্যবহার করা

একাধিক Gradle মডিউল সহ একটি প্রকল্প একটি মাল্টি-মডিউল প্রকল্প হিসাবে পরিচিত। একটি মাল্টি-মডিউল প্রজেক্টে যেটি কোনো বৈশিষ্ট্য মডিউল ছাড়াই একটি একক APK হিসেবে পাঠানো হয়, এটি একটি app মডিউল থাকা সাধারণ বিষয় যেটি আপনার প্রকল্পের বেশিরভাগ মডিউলের উপর নির্ভর করতে পারে এবং একটি base বা core মডিউল যার উপর বাকি মডিউলগুলি সাধারণত নির্ভর করে। app মডিউলে সাধারণত আপনার Application ক্লাস থাকে, যেখানে base মডিউলে আপনার প্রোজেক্টের সমস্ত মডিউল জুড়ে শেয়ার করা সমস্ত সাধারণ ক্লাস থাকে।

app মডিউল আপনার অ্যাপ্লিকেশন উপাদান (উদাহরণস্বরূপ, নীচের ছবিতে ApplicationComponent ) ঘোষণা করার জন্য একটি ভাল জায়গা যা অন্যান্য উপাদানগুলির পাশাপাশি আপনার অ্যাপের একক টনগুলির প্রয়োজন হতে পারে এমন বস্তুগুলি সরবরাহ করতে পারে৷ উদাহরণ স্বরূপ, OkHttpClient , JSON পার্সার, আপনার ডাটাবেসের অ্যাক্সেসর বা SharedPreferences অবজেক্টের মতো ক্লাস যা core মডিউলে সংজ্ঞায়িত করা যেতে পারে, app মডিউলে সংজ্ঞায়িত ApplicationComponent দ্বারা প্রদান করা হবে।

app মডিউলে, আপনার আয়ু কম সহ অন্যান্য উপাদানও থাকতে পারে। একটি উদাহরণ হতে পারে একটি ব্যবহারকারী-নির্দিষ্ট কনফিগারেশন সহ একটি UserComponent (যেমন একটি UserSession ) লগ ইন করার পরে৷

আপনার প্রজেক্টের বিভিন্ন মডিউলে, আপনি অন্তত একটি সাবকম্পোনেন্টকে সংজ্ঞায়িত করতে পারেন যেটি সেই মডিউলের জন্য নির্দিষ্ট যুক্তি আছে যেমন চিত্র 1 এ দেখা গেছে।

চিত্র 1. একটি বহু-মডিউল প্রকল্পে একটি ড্যাগার গ্রাফের উদাহরণ

উদাহরণস্বরূপ, একটি login মডিউলে, আপনার কাছে একটি কাস্টম @ModuleScope টীকা সহ একটি LoginComponent স্কোপ থাকতে পারে যা সেই বৈশিষ্ট্যের সাধারণ বস্তু যেমন একটি LoginRepository প্রদান করতে পারে। সেই মডিউলের অভ্যন্তরে, আপনার কাছে অন্যান্য উপাদান থাকতে পারে যা একটি ভিন্ন কাস্টম সুযোগ সহ একটি LoginComponent উপর নির্ভর করে, উদাহরণস্বরূপ @FeatureScope একটি LoginActivityComponent বা TermsAndConditionsComponent যেখানে আপনি আরও বৈশিষ্ট্য-নির্দিষ্ট যুক্তি যেমন ViewModel অবজেক্টের সুযোগ পেতে পারেন৷

Registration মতো অন্যান্য মডিউলগুলির জন্য, আপনার একই রকম সেটআপ থাকবে।

একটি মাল্টি-মডিউল প্রকল্পের জন্য একটি সাধারণ নিয়ম হল যে একই স্তরের মডিউলগুলি একে অপরের উপর নির্ভর করবে না। যদি তারা তা করে তবে বিবেচনা করুন যে ভাগ করা যুক্তি (তাদের মধ্যে নির্ভরতা) প্যারেন্ট মডিউলের অংশ হওয়া উচিত কিনা। যদি তাই হয়, ক্লাসগুলিকে প্যারেন্ট মডিউলে সরানোর জন্য রিফ্যাক্টর; যদি না হয়, একটি নতুন মডিউল তৈরি করুন যা প্যারেন্ট মডিউলকে প্রসারিত করে এবং উভয় মূল মডিউল নতুন মডিউলকে প্রসারিত করে।

একটি সর্বোত্তম অনুশীলন হিসাবে, আপনি সাধারণত নিম্নলিখিত ক্ষেত্রে একটি মডিউলে একটি উপাদান তৈরি করবেন:

  • LoginActivityComponent এর মতো আপনাকে ফিল্ড ইনজেকশন করতে হবে।

  • LoginComponent এর মতো আপনাকে অবজেক্ট স্কোপ করতে হবে।

যদি এই মামলাগুলির কোনটিই প্রযোজ্য না হয় এবং আপনাকে ড্যাগারকে বলতে হবে যে কীভাবে সেই মডিউল থেকে বস্তুগুলি সরবরাহ করতে হয়, সেই ক্লাসগুলির জন্য নির্মাণ ইনজেকশন সম্ভব না হলে @Provides বা @Binds পদ্ধতি সহ একটি ড্যাগার মডিউল তৈরি করুন এবং প্রকাশ করুন৷

ড্যাগার সাবকম্পোনেন্টের সাথে বাস্তবায়ন

অ্যান্ড্রয়েড অ্যাপের ডক পৃষ্ঠায় ড্যাগার ব্যবহার করে কীভাবে সাবকম্পোনেন্ট তৈরি এবং ব্যবহার করতে হয় তা কভার করে। যাইহোক, আপনি একই কোড ব্যবহার করতে পারবেন না কারণ বৈশিষ্ট্য মডিউলগুলি app মডিউল সম্পর্কে জানে না। একটি উদাহরণ হিসাবে, আপনি যদি একটি সাধারণ লগইন প্রবাহ এবং আমাদের পূর্ববর্তী পৃষ্ঠায় থাকা কোড সম্পর্কে চিন্তা করেন তবে এটি আর কম্পাইল করে না:

কোটলিন

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)
    ...
  }
}

জাভা

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 সম্পর্কে জানে না। এটি কাজ করার জন্য, আপনাকে বৈশিষ্ট্য মডিউলে একটি ইন্টারফেস সংজ্ঞায়িত করতে হবে যা একটি FeatureComponent প্রদান করে যা MyApplication প্রয়োগ করতে হবে।

নিম্নলিখিত উদাহরণে, আপনি একটি LoginComponentProvider ইন্টারফেস সংজ্ঞায়িত করতে পারেন যা লগইন প্রবাহের জন্য login মডিউলে একটি LoginComponent প্রদান করে:

কোটলিন

interface LoginComponentProvider {
    fun provideLoginComponent(): LoginComponent
}

জাভা

public interface LoginComponentProvider {
   public LoginComponent provideLoginComponent();
}

এখন, LoginActivity উপরে সংজ্ঞায়িত কোডের স্নিপেটের পরিবর্তে সেই ইন্টারফেসটি ব্যবহার করবে:

কোটলিন

class LoginActivity: Activity() {
  ...

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

    loginComponent.inject(this)
    ...
  }
}

জাভা

public class LoginActivity extends Activity {
    ...

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

        loginComponent.inject(this);

        ...
    }
}

এখন, MyApplication সেই ইন্টারফেসটি বাস্তবায়ন করতে হবে এবং প্রয়োজনীয় পদ্ধতিগুলি বাস্তবায়ন করতে হবে:

কোটলিন

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()
  }
}

জাভা

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 মডিউলের উপর নির্ভর করে। মডিউলগুলি কীভাবে গঠন করা হয় তার উপস্থাপনার জন্য চিত্র 2 দেখুন।

চিত্র 2. বৈশিষ্ট্য মডিউল সহ একটি প্রকল্পে একটি ড্যাগার গ্রাফের উদাহরণ

ড্যাগারে, উপাদানগুলিকে তাদের সাবকম্পোনেন্টগুলি সম্পর্কে জানতে হবে। এই তথ্যটি প্যারেন্ট কম্পোনেন্টে যোগ করা একটি ড্যাগার মডিউলে অন্তর্ভুক্ত করা হয়েছে ( অ্যান্ড্রয়েড অ্যাপে ড্যাগার ব্যবহারে SubcomponentsModule মডিউলের মতো)।

দুর্ভাগ্যবশত, অ্যাপ এবং বৈশিষ্ট্য মডিউলের মধ্যে বিপরীত নির্ভরতার সাথে, সাবকম্পোনেন্ট app মডিউল থেকে দৃশ্যমান নয় কারণ এটি বিল্ড পাথে নেই। একটি উদাহরণ হিসাবে, একটি login বৈশিষ্ট্য মডিউলে সংজ্ঞায়িত একটি LoginComponent app মডিউলে সংজ্ঞায়িত ApplicationComponent এর একটি সাবকম্পোনেন্ট হতে পারে না।

ড্যাগারের উপাদান নির্ভরতা নামক একটি প্রক্রিয়া রয়েছে যা আপনি এই সমস্যাটি সমাধান করতে ব্যবহার করতে পারেন। শিশু উপাদানটি পিতামাতার উপাদানের একটি সাবকম্পোনেন্ট হওয়ার পরিবর্তে, শিশু উপাদানটি পিতামাতার উপাদানের উপর নির্ভরশীল। তার সাথে পিতা-মাতার সম্পর্ক নেই; এখন উপাদানগুলি নির্দিষ্ট নির্ভরতা পেতে অন্যের উপর নির্ভর করে। নির্ভরশীল উপাদানগুলি ব্যবহার করার জন্য উপাদানগুলিকে গ্রাফ থেকে প্রকারগুলি প্রকাশ করতে হবে।

উদাহরণস্বরূপ: login নামক একটি বৈশিষ্ট্য মডিউল একটি LoginComponent তৈরি করতে চায় যা app গ্রেডল মডিউলে উপলব্ধ AppComponent উপর নির্ভর করে।

নীচে ক্লাস এবং AppComponent সংজ্ঞা রয়েছে যা app গ্রেডল মডিউলের অংশ:

কোটলিন

// 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 { ... }

জাভা

// 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 গ্রেডল মডিউলে যেটিতে app গ্রেডল মডিউল অন্তর্ভুক্ত রয়েছে, আপনার একটি LoginActivity আছে যা ইনজেকশনের জন্য একটি LoginViewModel দৃষ্টান্ত প্রয়োজন:

কোটলিন

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

জাভা

// 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 এর উপর নির্ভর করে:

কোটলিন

// 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)
}

জাভা

// 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 Dagger দ্বারা ইনজেকশন করা হবে, ইন্টারফেসে inject() মেথড যোগ করুন।

একটি LoginComponent তৈরি করার সময়, AppComponent এর একটি উদাহরণ পাস করতে হবে৷ এটি করতে উপাদান কারখানা ব্যবহার করুন:

কোটলিন

@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)
}

জাভা

@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() পদ্ধতিতে কল করতে পারে।

কোটলিন

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
    }
}

জাভা

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 এর ইন্টারফেসে এটি প্রকাশ করতে হবে:

কোটলিন

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

জাভা

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

নির্ভরশীল উপাদানগুলির সাথে স্কোপিং নিয়মগুলি সাব-কম্পোনেন্টগুলির মতো একইভাবে কাজ করে। যেহেতু LoginComponent AppComponent এর একটি উদাহরণ ব্যবহার করে, তারা একই সুযোগের টীকা ব্যবহার করতে পারে না।

আপনি যদি LoginViewModel LoginComponent এ স্কোপ করতে চান, তাহলে আপনি কাস্টম @ActivityScope টীকা ব্যবহার করে আগের মতই করবেন।

কোটলিন

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

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

জাভা

@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 মডিউলগুলির জন্য যেগুলিকে ইউটিলিটি বা সাহায্যকারী হিসাবে বোঝানো হয় এবং একটি গ্রাফ তৈরি করার প্রয়োজন নেই (এজন্য আপনার একটি ড্যাগার উপাদান প্রয়োজন), সেই ক্লাসগুলির @Provides এবং @Binds পদ্ধতিগুলির সাথে পাবলিক ড্যাগার মডিউলগুলি তৈরি করুন এবং প্রকাশ করুন কনস্ট্রাক্টর ইনজেকশন সমর্থন করে না।

  • ফিচার মডিউল সহ একটি অ্যান্ড্রয়েড অ্যাপে ড্যাগার ব্যবহার করতে, app মডিউলে সংজ্ঞায়িত ApplicationComponent দ্বারা প্রদত্ত নির্ভরতা অ্যাক্সেস করতে সক্ষম হতে উপাদান নির্ভরতা ব্যবহার করুন।