অ্যান্ড্রয়েড অ্যাপে ড্যাগার ব্যবহার করা

ড্যাগার বেসিক পৃষ্ঠাটি ব্যাখ্যা করেছে যে কীভাবে ড্যাগার আপনাকে আপনার অ্যাপে নির্ভরতা ইনজেকশন স্বয়ংক্রিয়ভাবে সাহায্য করতে পারে। ড্যাগারের সাথে, আপনাকে ক্লান্তিকর এবং ত্রুটি-প্রবণ বয়লারপ্লেট কোড লিখতে হবে না।

সর্বোত্তম অনুশীলনের সারাংশ

  • যখনই সম্ভব ড্যাগার গ্রাফে প্রকার যোগ করতে @Inject এর সাথে কনস্ট্রাক্টর ইনজেকশন ব্যবহার করুন। যখন এটি না হয়:
    • ড্যাগারকে বলতে @Binds ব্যবহার করুন কোন ইন্টারফেসের বাস্তবায়ন হওয়া উচিত।
    • আপনার প্রকল্পের মালিকানাধীন নয় এমন ক্লাসগুলি কীভাবে প্রদান করবেন তা ড্যাগারকে জানাতে @Provides ব্যবহার করুন।
  • আপনার একটি উপাদানে একবার মডিউল ঘোষণা করা উচিত।
  • যেখানে টীকাটি ব্যবহার করা হয়েছে তার জীবনকালের উপর নির্ভর করে সুযোগের টীকাগুলির নাম দিন। উদাহরণগুলির মধ্যে রয়েছে @ApplicationScope , @LoggedUserScope , এবং @ActivityScope

নির্ভরতা যোগ করা হচ্ছে

আপনার প্রকল্পে ড্যাগার ব্যবহার করতে, আপনার build.gradle ফাইলে আপনার অ্যাপ্লিকেশনে এই নির্ভরতা যোগ করুন। আপনি এই GitHub প্রকল্পে Dagger এর সর্বশেষ সংস্করণ খুঁজে পেতে পারেন।

কোটলিন

plugins {
  id 'kotlin-kapt'
}

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    kapt 'com.google.dagger:dagger-compiler:2.x'
}

জাভা

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}

অ্যান্ড্রয়েডে ড্যাগার

চিত্র 1 থেকে নির্ভরতা গ্রাফ সহ একটি উদাহরণ Android অ্যাপ বিবেচনা করুন।

লগইনঅ্যাক্টিভিটি LoginViewModel-এর উপর নির্ভর করে, যা UserRepository-এর উপর নির্ভর করে, যা UserLocalDataSource এবং UserRemoteDataSource-এর উপর নির্ভর করে, যা রেট্রোফিটের উপর নির্ভর করে।

চিত্র 1. উদাহরণ কোডের নির্ভরতা গ্রাফ

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

কারণ যে ইন্টারফেসটি গ্রাফ তৈরি করে সেটি @Component দিয়ে টীকা করা হয়েছে, আপনি এটিকে ApplicationComponent বা ApplicationGraph বলতে পারেন। আপনি সাধারণত আপনার কাস্টম Application ক্লাসে সেই উপাদানটির একটি উদাহরণ রাখেন এবং আপনার অ্যাপ্লিকেশন গ্রাফের প্রয়োজন হলে প্রতিবার এটিকে কল করুন, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

কোটলিন

// Definition of the Application graph
@Component
interface ApplicationComponent { ... }

// appComponent lives in the Application class to share its lifecycle
class MyApplication: Application() {
    // Reference to the application graph that is used across the whole app
    val appComponent = DaggerApplicationComponent.create()
}

জাভা

// Definition of the Application graph
@Component
public interface ApplicationComponent {
}

// appComponent lives in the Application class to share its lifecycle
public class MyApplication extends Application {

    // Reference to the application graph that is used across the whole app
    ApplicationComponent appComponent = DaggerApplicationComponent.create();
}

যেহেতু কিছু নির্দিষ্ট অ্যান্ড্রয়েড ফ্রেমওয়ার্ক ক্লাস যেমন অ্যাক্টিভিটি এবং ফ্র্যাগমেন্টগুলি সিস্টেম দ্বারা তাত্ক্ষণিক করা হয়, ড্যাগার আপনার জন্য সেগুলি তৈরি করতে পারে না৷ বিশেষত কার্যকলাপের জন্য, যেকোনো প্রারম্ভিক কোডকে onCreate() পদ্ধতিতে যেতে হবে। তার মানে আপনি ক্লাসের কনস্ট্রাক্টরে (কন্সট্রাক্টর ইনজেকশন) @Inject টীকাটি ব্যবহার করতে পারবেন না যেমন আপনি আগের উদাহরণগুলিতে করেছিলেন। পরিবর্তে, আপনাকে ফিল্ড ইনজেকশন ব্যবহার করতে হবে।

onCreate() পদ্ধতিতে একটি কার্যকলাপের জন্য প্রয়োজনীয় নির্ভরতা তৈরি করার পরিবর্তে, আপনি চান যে ড্যাগার আপনার জন্য সেই নির্ভরতাগুলি পূরণ করুক। ফিল্ড ইনজেকশনের জন্য, আপনি পরিবর্তে ড্যাগার গ্রাফ থেকে যে ক্ষেত্রগুলি পেতে চান সেগুলিতে @Inject টীকা প্রয়োগ করুন৷

কোটলিন

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel
}

জাভা

public class LoginActivity extends Activity {

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

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

ড্যাগারের সাথে বিবেচনার একটি হল যে ইনজেকশনের ক্ষেত্রগুলি ব্যক্তিগত হতে পারে না। পূর্ববর্তী কোডের মতো তাদের কমপক্ষে প্যাকেজ-ব্যক্তিগত দৃশ্যমানতা থাকতে হবে।

ইনজেকশন কার্যক্রম

ড্যাগারকে জানা দরকার যে LoginActivity এর প্রয়োজনীয় ViewModel প্রদান করার জন্য গ্রাফটি অ্যাক্সেস করতে হবে। ড্যাগার বেসিক পৃষ্ঠায়, আপনি গ্রাফ থেকে যা পেতে চান তার রিটার্ন টাইপের সাথে ফাংশন প্রকাশ করে গ্রাফ থেকে অবজেক্ট পেতে @Component ইন্টারফেস ব্যবহার করেছেন। এই ক্ষেত্রে, আপনাকে ড্যাগারকে এমন একটি বস্তু সম্পর্কে বলতে হবে ( এই ক্ষেত্রে LoginActivity ) যার জন্য ইনজেকশনের জন্য নির্ভরতা প্রয়োজন। এর জন্য, আপনি এমন একটি ফাংশন উন্মোচন করুন যা একটি প্যারামিটার হিসাবে অবজেক্টটি নেয় যা ইনজেকশনের অনুরোধ করে।

কোটলিন

@Component
interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is requesting.
    fun inject(activity: LoginActivity)
}

জাভা

@Component
public interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is injecting.
    void inject(LoginActivity loginActivity);
}

এই ফাংশনটি ড্যাগারকে বলে যে LoginActivity গ্রাফ অ্যাক্সেস করতে চায় এবং ইনজেকশনের অনুরোধ করে। ড্যাগারকে সমস্ত নির্ভরতা পূরণ করতে হবে যা LoginActivity জন্য প্রয়োজন ( LoginViewModel এর নিজস্ব নির্ভরতা সহ)। আপনার যদি একাধিক ক্লাস থাকে যা ইনজেকশনের জন্য অনুরোধ করে, তাহলে আপনাকে নির্দিষ্টভাবে সেগুলিকে কম্পোনেন্টে তাদের সঠিক প্রকারের সাথে ঘোষণা করতে হবে। উদাহরণস্বরূপ, যদি আপনার কাছে LoginActivity এবং RegistrationActivity ইনজেকশনের অনুরোধ থাকে, তাহলে আপনার কাছে দুটি inject() পদ্ধতির পরিবর্তে একটি সাধারণ পদ্ধতি থাকবে যা উভয় ক্ষেত্রেই কভার করবে। একটি জেনেরিক inject() পদ্ধতি ড্যাগারকে কী সরবরাহ করতে হবে তা বলে না। ইন্টারফেসের ফাংশনগুলির যে কোনও নাম থাকতে পারে, কিন্তু যখন তারা একটি প্যারামিটার হিসাবে ইনজেক্ট করার জন্য অবজেক্ট গ্রহণ করে তখন সেগুলিকে inject() বলা ড্যাগারের একটি নিয়ম।

ক্রিয়াকলাপে একটি বস্তুকে ইনজেকশন করতে, আপনি আপনার Application ক্লাসে সংজ্ঞায়িত appComponent ব্যবহার করবেন এবং inject() পদ্ধতিতে কল করবেন, যে ক্রিয়াকলাপটি ইনজেকশনের অনুরোধ করে তার একটি উদাহরণ দিয়ে।

ক্রিয়াকলাপগুলি ব্যবহার করার সময়, খণ্ড পুনরুদ্ধারের সমস্যা এড়াতে super.onCreate() কল করার আগে কার্যকলাপের onCreate() পদ্ধতিতে ড্যাগার ইনজেকশন করুন। super.onCreate() এ পুনরুদ্ধার পর্বের সময়, একটি কার্যকলাপ এমন অংশ সংযুক্ত করে যা কার্যকলাপ বাইন্ডিং অ্যাক্সেস করতে চায়।

টুকরো ব্যবহার করার সময়, খণ্ডের onAttach() পদ্ধতিতে ড্যাগার ইনজেকশন করুন। এই ক্ষেত্রে, super.onAttach() কল করার আগে বা পরে এটি করা যেতে পারে।

কোটলিন

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        (applicationContext as MyApplication).appComponent.inject(this)
        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

// @Inject tells Dagger how to create instances of LoginViewModel
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

জাভা

public class LoginActivity extends Activity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        ((MyApplication) getApplicationContext()).appComponent.inject(this);
        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

public class LoginViewModel {

    private final UserRepository userRepository;

    // @Inject tells Dagger how to create instances of LoginViewModel
    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

গ্রাফ তৈরি করার জন্য বাকি নির্ভরতাগুলি কীভাবে সরবরাহ করবেন তা ড্যাগারকে বলি:

কোটলিন

class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor(
    private val loginService: LoginRetrofitService
) { ... }

জাভা

public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

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

public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {}
}

public class UserRemoteDataSource {

    private final LoginRetrofitService loginRetrofitService;

    @Inject
    public UserRemoteDataSource(LoginRetrofitService loginRetrofitService) {
        this.loginRetrofitService = loginRetrofitService;
    }
}

ড্যাগার মডিউল

এই উদাহরণের জন্য, আপনি Retrofit নেটওয়ার্কিং লাইব্রেরি ব্যবহার করছেন। UserRemoteDataSource LoginRetrofitService এর উপর নির্ভরশীলতা রয়েছে। যাইহোক, LoginRetrofitService এর একটি উদাহরণ তৈরি করার উপায় আপনি এখন পর্যন্ত যা করছেন তার থেকে আলাদা। এটি একটি ক্লাস ইনস্ট্যান্টেশন নয়; এটি Retrofit.Builder() কল করার এবং লগইন পরিষেবা কনফিগার করার জন্য বিভিন্ন প্যারামিটারে পাস করার ফলাফল।

@Inject টীকা ছাড়াও, Dagger কে কিভাবে ক্লাসের একটি উদাহরণ প্রদান করতে হয় তা বলার আরেকটি উপায় আছে: Dagger মডিউলের ভিতরের তথ্য। একটি ড্যাগার মডিউল হল একটি ক্লাস যা @Module দিয়ে টীকা করা হয়। সেখানে, আপনি @Provides টীকা দিয়ে নির্ভরতা নির্ধারণ করতে পারেন।

কোটলিন

// @Module informs Dagger that this class is a Dagger Module
@Module
class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService::class.java)
    }
}

জাভা

// @Module informs Dagger that this class is a Dagger Module
@Module
public class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return new Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService.class);
    }
}

মডিউলগুলি হল একটি উপায় যা শব্দার্থগতভাবে কীভাবে বস্তুগুলি সরবরাহ করতে হয় সে সম্পর্কে তথ্য এনক্যাপসুলেট করে। আপনি দেখতে পাচ্ছেন, আপনি নেটওয়ার্কিং সম্পর্কিত বস্তু প্রদানের লজিক গ্রুপ করার জন্য NetworkModule ক্লাসকে কল করেছেন। যদি অ্যাপ্লিকেশনটি প্রসারিত হয়, তাহলে আপনি এখানে কীভাবে একটি OkHttpClient প্রদান করবেন বা কীভাবে Gson বা Moshi কনফিগার করবেন তা যোগ করতে পারেন।

@Provides পদ্ধতির নির্ভরতা হল সেই পদ্ধতির পরামিতি। পূর্ববর্তী পদ্ধতির জন্য, LoginRetrofitService কোনো নির্ভরতা ছাড়াই প্রদান করা যেতে পারে কারণ পদ্ধতিটির কোনো পরামিতি নেই। আপনি যদি প্যারামিটার হিসাবে একটি OkHttpClient ঘোষণা করে থাকেন, তাহলে LoginRetrofitService এর নির্ভরতা পূরণ করতে Dagger-কে গ্রাফ থেকে একটি OkHttpClient উদাহরণ প্রদান করতে হবে। উদাহরণ স্বরূপ:

কোটলিন

@Module
class NetworkModule {
    // Hypothetical dependency on LoginRetrofitService
    @Provides
    fun provideLoginRetrofitService(
        okHttpClient: OkHttpClient
    ): LoginRetrofitService { ... }
}

জাভা

@Module
public class NetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService(OkHttpClient okHttpClient) {
        ...
    }
}

এই মডিউল সম্পর্কে ড্যাগার গ্রাফটি জানার জন্য, আপনাকে এটিকে @Component ইন্টারফেসে নিম্নরূপ যোগ করতে হবে:

কোটলিন

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    ...
}

জাভা

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    ...
}

ড্যাগার গ্রাফে প্রকার যোগ করার প্রস্তাবিত উপায় হল কনস্ট্রাক্টর ইনজেকশন ব্যবহার করে (যেমন ক্লাসের কনস্ট্রাক্টরের উপর @Inject টীকা সহ)। কখনও কখনও, এটি সম্ভব হয় না এবং আপনাকে ড্যাগার মডিউল ব্যবহার করতে হবে। একটি উদাহরণ হল যখন আপনি ড্যাগারকে একটি গণনার ফলাফল ব্যবহার করতে চান কিভাবে একটি বস্তুর একটি উদাহরণ তৈরি করতে হয়। যখনই এটিকে সেই ধরনের একটি উদাহরণ প্রদান করতে হয়, ড্যাগার কোডটি @Provides পদ্ধতির ভিতরে চালায়।

উদাহরণের ড্যাগার গ্রাফটি এখন এইভাবে দেখায়:

লগইন অ্যাক্টিভিটি নির্ভরতা গ্রাফের ডায়াগ্রাম

চিত্র 2. ড্যাগার দ্বারা ইনজেকশন দেওয়া LoginActivity সহ গ্রাফের উপস্থাপনা

গ্রাফের এন্ট্রি পয়েন্ট হল LoginActivity . যেহেতু LoginActivity LoginViewModel ইনজেক্ট করে, ড্যাগার এমন একটি গ্রাফ তৈরি করে যা জানে কিভাবে LoginViewModel এর একটি উদাহরণ প্রদান করতে হয়, এবং পুনরাবৃত্তিমূলকভাবে, এর নির্ভরতা। ক্লাসের কনস্ট্রাক্টরের @Inject টীকাটির কারণে ড্যাগার কীভাবে এটি করতে হয় তা জানেন।

ড্যাগার দ্বারা উত্পন্ন ApplicationComponent ভিতরে, এটি কীভাবে প্রদান করতে জানে সেগুলি সমস্ত ক্লাসের উদাহরণ পেতে একটি ফ্যাক্টরি-টাইপ পদ্ধতি রয়েছে৷ এই উদাহরণে, Dagger LoginRetrofitService এর একটি উদাহরণ পেতে ApplicationComponent এ অন্তর্ভুক্ত NetworkModule এ প্রতিনিধি করে।

ড্যাগার স্কোপস

স্কোপগুলি একটি উপাদানের একটি প্রকারের একটি অনন্য উদাহরণের উপায় হিসাবে ড্যাগার বেসিক পৃষ্ঠায় উল্লেখ করা হয়েছিল। কম্পোনেন্টের লাইফসাইকেলে একটি টাইপকে স্কোপ করার দ্বারা এটাই বোঝানো হয়।

যেহেতু আপনি অ্যাপের অন্যান্য বৈশিষ্ট্যগুলিতে UserRepository ব্যবহার করতে চাইতে পারেন এবং প্রতিবার এটির প্রয়োজনে একটি নতুন বস্তু তৈরি করতে নাও পারেন, আপনি এটিকে পুরো অ্যাপের জন্য একটি অনন্য উদাহরণ হিসাবে মনোনীত করতে পারেন। এটি LoginRetrofitService এর জন্য একই: এটি তৈরি করা ব্যয়বহুল হতে পারে, এবং আপনি সেই বস্তুর একটি অনন্য উদাহরণও পুনরায় ব্যবহার করতে চান। UserRemoteDataSource এর একটি উদাহরণ তৈরি করা এত ব্যয়বহুল নয়, তাই এটিকে উপাদানের জীবনচক্রে স্কোপ করার প্রয়োজন নেই।

@Singleton হল একমাত্র সুযোগ টীকা যা javax.inject প্যাকেজের সাথে আসে। আপনি ApplicationComponent এবং যে বস্তুগুলি আপনি পুরো অ্যাপ্লিকেশন জুড়ে পুনরায় ব্যবহার করতে চান তা টীকা করতে এটি ব্যবহার করতে পারেন।

কোটলিন

@Singleton
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    fun inject(activity: LoginActivity)
}

@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

@Module
class NetworkModule {
    // Way to scope types inside a Dagger Module
    @Singleton
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService { ... }
}

জাভা

@Singleton
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    void inject(LoginActivity loginActivity);
}

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

@Module
public class NetworkModule {

    @Singleton
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() { ... }
}

অবজেক্টে স্কোপ প্রয়োগ করার সময় মেমরি লিক প্রবর্তন না করার যত্ন নিন। যতক্ষণ পর্যন্ত স্কোপড উপাদানটি মেমরিতে থাকে, ততক্ষণ তৈরি করা বস্তুটিও মেমরিতে থাকে। কারণ অ্যাপটি চালু হলে ( Application ক্লাসে) ApplicationComponent তৈরি হয়, অ্যাপটি ধ্বংস হয়ে গেলে এটি ধ্বংস হয়ে যায়। সুতরাং, অ্যাপ্লিকেশনটি ধ্বংস না হওয়া পর্যন্ত UserRepository এর অনন্য উদাহরণ সর্বদা মেমরিতে থাকে।

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

যদি আপনার লগইন ফ্লো (একক LoginActivity দ্বারা পরিচালিত) একাধিক খণ্ড নিয়ে গঠিত, তাহলে আপনাকে সমস্ত খণ্ডে LoginViewModel এর একই উদাহরণ পুনরায় ব্যবহার করা উচিত। @Singleton নিম্নলিখিত কারণগুলির জন্য উদাহরণটি পুনরায় ব্যবহার করতে LoginViewModel টীকা করতে পারে না:

  1. ফ্লো শেষ হওয়ার পরে LoginViewModel এর উদাহরণ মেমরিতে থাকবে।

  2. আপনি প্রতিটি লগইন প্রবাহের জন্য LoginViewModel এর একটি ভিন্ন উদাহরণ চান। উদাহরণ স্বরূপ, ব্যবহারকারী লগ আউট করলে, আপনি LoginViewModel এর একটি ভিন্ন দৃষ্টান্ত চান, ব্যবহারকারী প্রথমবার লগ ইন করার সময় একই উদাহরণের পরিবর্তে।

LoginActivity এর জীবনচক্রে LoginViewModel সুযোগ পেতে আপনাকে লগইন প্রবাহ এবং একটি নতুন সুযোগের জন্য একটি নতুন উপাদান (একটি নতুন সাবগ্রাফ) তৈরি করতে হবে৷

লগইন প্রবাহের জন্য নির্দিষ্ট একটি গ্রাফ তৈরি করা যাক।

কোটলিন

@Component
interface LoginComponent {}

জাভা

@Component
public interface LoginComponent {
}

এখন, LoginActivity LoginComponent থেকে ইনজেকশন পাওয়া উচিত কারণ এতে একটি লগইন-নির্দিষ্ট কনফিগারেশন রয়েছে। এটি ApplicationComponent ক্লাস থেকে LoginActivity ইনজেক্ট করার দায়িত্ব সরিয়ে দেয়।

কোটলিন

@Component
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

জাভা

@Component
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

LoginComponent অবশ্যই ApplicationComponent থেকে অবজেক্ট অ্যাক্সেস করতে সক্ষম হবে কারণ LoginViewModel UserRepository উপর নির্ভর করে। Dagger কে বলার উপায় যে আপনি একটি নতুন কম্পোনেন্টকে অন্য কম্পোনেন্টের অংশ ব্যবহার করতে চান তা হল Dagger সাবকম্পোনেন্টস । নতুন কম্পোনেন্ট অবশ্যই শেয়ার্ড রিসোর্স ধারণকারী কম্পোনেন্টের একটি সাবকম্পোনেন্ট হতে হবে।

সাবকম্পোনেন্ট হল এমন উপাদান যা একটি প্যারেন্ট কম্পোনেন্টের অবজেক্ট গ্রাফকে উত্তরাধিকারী করে এবং প্রসারিত করে। এইভাবে, প্যারেন্ট কম্পোনেন্টে প্রদত্ত সমস্ত অবজেক্ট সাবকম্পোনেন্টেও দেওয়া হয়। এইভাবে, একটি উপ-কম্পোনেন্ট থেকে একটি বস্তু মূল উপাদান দ্বারা প্রদত্ত একটি বস্তুর উপর নির্ভর করতে পারে।

সাবকম্পোনেন্টের দৃষ্টান্ত তৈরি করতে, আপনাকে প্যারেন্ট কম্পোনেন্টের একটি উদাহরণ প্রয়োজন। তাই, সাবকম্পোনেন্টে প্যারেন্ট কম্পোনেন্ট দ্বারা প্রদত্ত অবজেক্টগুলি এখনও প্যারেন্ট কম্পোনেন্টে স্কোপ করা হয়েছে।

উদাহরণে, আপনাকে অবশ্যই LoginComponent ApplicationComponent এর একটি সাবকম্পোনেন্ট হিসেবে সংজ্ঞায়িত করতে হবে। এটি করার জন্য, @Subcomponent এর সাথে LoginComponent টীকা করুন:

কোটলিন

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    fun inject(loginActivity: LoginActivity)
}

জাভা

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
public interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    void inject(LoginActivity loginActivity);
}

আপনাকে অবশ্যই LoginComponent ভিতরে একটি সাব-কম্পোনেন্ট ফ্যাক্টরি সংজ্ঞায়িত করতে হবে যাতে ApplicationComponent জানে কিভাবে LoginComponent এর উদাহরণ তৈরি করতে হয়।

কোটলিন

@Subcomponent
interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    fun inject(loginActivity: LoginActivity)
}

জাভা

@Subcomponent
public interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    void inject(LoginActivity loginActivity);
}

Dagger কে জানাতে যে LoginComponent হল ApplicationComponent এর একটি সাবকম্পোনেন্ট, আপনাকে এটি দ্বারা নির্দেশ করতে হবে:

  1. একটি নতুন ড্যাগার মডিউল তৈরি করা (যেমন SubcomponentsModule ) সাবকম্পোনেন্টের ক্লাসকে টীকাটির subcomponents অ্যাট্রিবিউটে পাস করা।

    কোটলিন

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent::class)
    class SubcomponentsModule {}
    

    জাভা

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent.class)
    public class SubcomponentsModule {
    }
    
  2. ApplicationComponent এ নতুন মডিউল (যেমন SubcomponentsModule ) যোগ করা হচ্ছে:

    কোটলিন

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    }
    

    জাভা

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = {NetworkModule.class, SubcomponentsModule.class})
    public interface ApplicationComponent {
    }
    

    মনে রাখবেন যে ApplicationComponent আর LoginActivity ইনজেক্ট করার দরকার নেই কারণ সেই দায়িত্ব এখন LoginComponent এর অন্তর্গত, তাই আপনি ApplicationComponent থেকে inject() পদ্ধতিটি সরিয়ে ফেলতে পারেন।

    ApplicationComponent এর ভোক্তাদের জানতে হবে কিভাবে LoginComponent এর উদাহরণ তৈরি করতে হয়। প্যারেন্ট কম্পোনেন্টকে অবশ্যই তার ইন্টারফেসে একটি পদ্ধতি যোগ করতে হবে যাতে ভোক্তাদের প্যারেন্ট কম্পোনেন্টের একটি উদাহরণ থেকে সাবকম্পোনেন্টের উদাহরণ তৈরি করতে দেয়:

  3. ইন্টারফেসে LoginComponent উদাহরণ তৈরি করে এমন কারখানাটি প্রকাশ করুন:

    কোটলিন

    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    fun loginComponent(): LoginComponent.Factory
    }
    

    জাভা

    @Singleton
    @Component(modules = { NetworkModule.class, SubcomponentsModule.class} )
    public interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    LoginComponent.Factory loginComponent();
    }
    

সাব-কম্পোনেন্টগুলিতে স্কোপ বরাদ্দ করা

আপনি যদি প্রকল্পটি তৈরি করেন, আপনি ApplicationComponent এবং LoginComponent উভয়ের উদাহরণ তৈরি করতে পারেন। ApplicationComponent অ্যাপ্লিকেশনটির জীবনচক্রের সাথে সংযুক্ত কারণ যতক্ষণ অ্যাপ্লিকেশনটি মেমরিতে থাকে ততক্ষণ আপনি গ্রাফের একই উদাহরণ ব্যবহার করতে চান৷

LoginComponent জীবনচক্র কি? আপনার LoginComponent প্রয়োজনের একটি কারণ হল লগইন-সম্পর্কিত টুকরোগুলির মধ্যে LoginViewModel একই উদাহরণ শেয়ার করা প্রয়োজন৷ কিন্তু এছাড়াও, যখনই একটি নতুন লগইন প্রবাহ থাকে তখন আপনি LoginViewModel এর বিভিন্ন উদাহরণ চান৷ LoginActivity হল LoginComponent এর জন্য সঠিক জীবনকাল: প্রতিটি নতুন ক্রিয়াকলাপের জন্য, আপনার LoginComponent এর একটি নতুন উদাহরণ এবং LoginComponent এর সেই দৃষ্টান্ত ব্যবহার করতে পারে এমন টুকরোগুলির প্রয়োজন৷

LoginComponent LoginActivity লাইফসাইকেলের সাথে সংযুক্ত থাকার কারণে, আপনাকে অ্যাক্টিভিটির কম্পোনেন্টের রেফারেন্স রাখতে হবে যেভাবে আপনি Application ক্লাসে applicationComponent রেফারেন্স রেখেছিলেন। এই ভাবে, টুকরা এটি অ্যাক্সেস করতে পারেন.

কোটলিন

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent
    ...
}

জাভা

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    ...
}

লক্ষ্য করুন যে ভেরিয়েবল loginComponent @Inject এর সাথে টীকা করা হয়নি কারণ আপনি আশা করছেন না যে ভেরিয়েবলটি Dagger দ্বারা সরবরাহ করা হবে।

আপনি LoginComponent একটি রেফারেন্স পেতে ApplicationComponent ব্যবহার করতে পারেন এবং তারপরে নিম্নরূপ LoginActivity ইনজেক্ট করতে পারেন:

কোটলিন

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    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)

        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

জাভা

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

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

        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

LoginComponent অ্যাক্টিভিটির onCreate() পদ্ধতিতে তৈরি করা হয়েছে, এবং যখন অ্যাক্টিভিটি ধ্বংস হয়ে যাবে তখন এটি অস্পষ্টভাবে ধ্বংস হয়ে যাবে।

LoginComponent অবশ্যই প্রতিবার অনুরোধ করার সময় LoginViewModel এর একই উদাহরণ প্রদান করতে হবে। আপনি একটি কাস্টম টীকা স্কোপ তৈরি করে এবং এটির সাথে LoginComponent এবং LoginViewModel উভয় টীকা দিয়ে এটি নিশ্চিত করতে পারেন। মনে রাখবেন আপনি @Singleton টীকাটি ব্যবহার করতে পারবেন না কারণ এটি ইতিমধ্যেই মূল উপাদান দ্বারা ব্যবহার করা হয়েছে এবং এটি বস্তুটিকে একটি অ্যাপ্লিকেশন সিঙ্গলটন (পুরো অ্যাপের জন্য অনন্য উদাহরণ) করে তুলবে। আপনাকে একটি ভিন্ন টীকা স্কোপ তৈরি করতে হবে।

এই ক্ষেত্রে, আপনি এই সুযোগটিকে @LoginScope কল করতে পারেন তবে এটি একটি ভাল অনুশীলন নয়। স্কোপ টীকাটির নামটি যে উদ্দেশ্য পূরণ করে তা স্পষ্ট হওয়া উচিত নয়। পরিবর্তে, এটির জীবনকালের উপর নির্ভর করে নামকরণ করা উচিত কারণ টীকাগুলি ভাইবোন উপাদান যেমন RegistrationComponent এবং SettingsComponent দ্বারা পুনরায় ব্যবহার করা যেতে পারে। এই কারণেই আপনাকে @LoginScope এর পরিবর্তে এটিকে @ActivityScope বলা উচিত।

কোটলিন

// Definition of a custom scope called ActivityScope
@Scope
@Retention(value = AnnotationRetention.RUNTIME)
annotation class ActivityScope

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

জাভা

// Definition of a custom scope called ActivityScope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
public interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

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

এখন, যদি আপনার দুটি টুকরো থাকে যার প্রয়োজন LoginViewModel , তাদের উভয়ই একই উদাহরণের সাথে সরবরাহ করা হয়। উদাহরণস্বরূপ, যদি আপনার একটি LoginUsernameFragment এবং একটি LoginPasswordFragment থাকে তাহলে তাদের LoginComponent দ্বারা ইনজেকশন করতে হবে:

কোটলিন

@ActivityScope
@Subcomponent
interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    fun inject(loginActivity: LoginActivity)
    fun inject(usernameFragment: LoginUsernameFragment)
    fun inject(passwordFragment: LoginPasswordFragment)
}

জাভা

@ActivityScope
@Subcomponent
public interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    void inject(LoginActivity loginActivity);
    void inject(LoginUsernameFragment loginUsernameFragment);
    void inject(LoginPasswordFragment loginPasswordFragment);
}

উপাদানগুলি LoginActivity অবজেক্টে থাকা উপাদানটির উদাহরণ অ্যাক্সেস করে। LoginUserNameFragment এর উদাহরণ কোড নিম্নলিখিত কোড স্নিপেটে প্রদর্শিত হয়:

কোটলিন

class LoginUsernameFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

জাভা

public class LoginUsernameFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

এবং LoginPasswordFragment এর জন্য একই:

কোটলিন

class LoginPasswordFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

জাভা

public class LoginPasswordFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

চিত্র 3 দেখায় কিভাবে ড্যাগার গ্রাফটি নতুন সাবকম্পোনেন্টের সাথে দেখায়। একটি সাদা বিন্দু সহ ক্লাসগুলি ( UserRepository , LoginRetrofitService , এবং LoginViewModel ) হল সেগুলি যেগুলির নিজস্ব উপাদানগুলির জন্য একটি অনন্য উদাহরণ রয়েছে৷

শেষ সাবকম্পোনেন্ট যোগ করার পরে অ্যাপ্লিকেশন গ্রাফ

চিত্র 3. Android অ্যাপের উদাহরণের জন্য আপনি যে গ্রাফটি তৈরি করেছেন তার উপস্থাপনা

আসুন গ্রাফের অংশগুলিকে ভেঙে দেওয়া যাক:

  1. NetworkModule (এবং সেইজন্য LoginRetrofitService ) ApplicationComponent এ অন্তর্ভুক্ত কারণ আপনি কম্পোনেন্টে এটি নির্দিষ্ট করেছেন।

  2. UserRepository ApplicationComponent এ রয়ে গেছে কারণ এটি ApplicationComponent এ স্কোপ করা হয়েছে। যদি প্রজেক্ট বাড়তে থাকে, আপনি বিভিন্ন বৈশিষ্ট্য জুড়ে একই উদাহরণ শেয়ার করতে চান (যেমন রেজিস্ট্রেশন)।

    যেহেতু UserRepository হল ApplicationComponent এর অংশ, তাই এর নির্ভরতা (যেমন UserLocalDataSource এবং UserRemoteDataSource ) এই কম্পোনেন্টে থাকতে হবে যাতে UserRepository এর উদাহরণ প্রদান করতে সক্ষম হয়।

  3. LoginViewModel LoginComponent এ অন্তর্ভুক্ত কারণ এটি শুধুমাত্র LoginComponent দ্বারা ইনজেকশন করা ক্লাসের জন্য প্রয়োজন। LoginViewModel ApplicationComponent এ অন্তর্ভুক্ত নয় কারণ ApplicationComponent এ কোন নির্ভরতা LoginViewModel প্রয়োজন নেই।

    একইভাবে, আপনি যদি UserRepository to ApplicationComponent এ স্কোপ না করে থাকেন, তাহলে Dagger স্বয়ংক্রিয়ভাবে LoginComponent এর অংশ হিসেবে UserRepository এবং এর নির্ভরতাকে অন্তর্ভুক্ত করত কারণ এটিই বর্তমানে UserRepository ব্যবহার করা হয়।

একটি ভিন্ন জীবনচক্রে অবজেক্ট স্কোপ করা ছাড়াও, সাব-কম্পোনেন্ট তৈরি করা একটি ভাল অভ্যাস যাতে আপনার অ্যাপ্লিকেশনের বিভিন্ন অংশ একে অপরের থেকে এনক্যাপসুলেট করা যায়

আপনার অ্যাপের প্রবাহের উপর নির্ভর করে বিভিন্ন ড্যাগার সাবগ্রাফ তৈরি করার জন্য আপনার অ্যাপকে স্ট্রাকচার করা মেমরি এবং স্টার্টআপ সময়ের পরিপ্রেক্ষিতে আরও কার্যকরী এবং মাপযোগ্য অ্যাপ্লিকেশনের দিকে সাহায্য করে।

একটি ড্যাগার গ্রাফ তৈরি করার সময় সর্বোত্তম অনুশীলন

আপনার অ্যাপ্লিকেশনের জন্য ড্যাগার গ্রাফ তৈরি করার সময়:

  • আপনি যখন একটি উপাদান তৈরি করেন, তখন সেই উপাদানটির জীবনকালের জন্য কোন উপাদান দায়ী তা বিবেচনা করা উচিত। এই ক্ষেত্রে, Application ক্লাস ApplicationComponent দায়িত্বে এবং LoginActivity LoginComponent এর দায়িত্বে থাকে।

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

ড্যাগার ব্যবহার করে এমন একটি প্রকল্প পরীক্ষা করা হচ্ছে

ড্যাগারের মতো নির্ভরতা ইনজেকশন ফ্রেমওয়ার্ক ব্যবহার করার সুবিধাগুলির মধ্যে একটি হল এটি আপনার কোড পরীক্ষা করা সহজ করে তোলে।

ইউনিট পরীক্ষা

ইউনিট পরীক্ষার জন্য আপনাকে ড্যাগার ব্যবহার করতে হবে না। কনস্ট্রাক্টর ইনজেকশন ব্যবহার করে এমন একটি ক্লাস পরীক্ষা করার সময়, আপনাকে সেই ক্লাসটি ইনস্ট্যান্টিয়েট করতে ড্যাগার ব্যবহার করতে হবে না। আপনি সরাসরি এটির কন্সট্রাক্টরকে নকল বা মক নির্ভরতায় পাসিংকে সরাসরি কল করতে পারেন ঠিক যেমনটি আপনি করতেন যদি সেগুলি টীকা করা না হয়।

উদাহরণস্বরূপ, LoginViewModel পরীক্ষা করার সময়:

কোটলিন

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

class LoginViewModelTest {

    @Test
    fun `Happy path`() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        val viewModel = LoginViewModel(fakeUserRepository)
        assertEquals(...)
    }
}

জাভা

@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

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

public class LoginViewModelTest {

    @Test
    public void happyPath() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        LoginViewModel viewModel = new LoginViewModel(fakeUserRepository);
        assertEquals(...);
    }
}

শেষ থেকে শেষ পরীক্ষা

ইন্টিগ্রেশন পরীক্ষার জন্য, একটি ভাল অনুশীলন হল পরীক্ষার জন্য একটি TestApplicationComponent তৈরি করা। উৎপাদন এবং পরীক্ষা একটি ভিন্ন উপাদান কনফিগারেশন ব্যবহার করে

এর জন্য আপনার অ্যাপ্লিকেশনে মডিউলগুলির আরও আপ-ফ্রন্ট ডিজাইন প্রয়োজন৷ পরীক্ষার উপাদান উৎপাদন উপাদান প্রসারিত করে এবং মডিউলগুলির একটি ভিন্ন সেট ইনস্টল করে।

কোটলিন

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = [FakeNetworkModule::class, SubcomponentsModule::class])
interface TestApplicationComponent : ApplicationComponent {
}

জাভা

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// Component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = {FakeNetworkModule.class, SubcomponentsModule.class})
public interface TestApplicationComponent extends ApplicationComponent {
}

FakeNetworkModule এর আসল NetworkModule একটি জাল বাস্তবায়ন রয়েছে। সেখানে আপনি যা কিছু প্রতিস্থাপন করতে চান তার জাল উদাহরণ বা উপহাস প্রদান করতে পারেন।

কোটলিন

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
class FakeNetworkModule {
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        return FakeLoginService()
    }
}

জাভা

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
public class FakeNetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        return new FakeLoginService();
    }
}

আপনার ইন্টিগ্রেশন বা এন্ড-টু-এন্ড পরীক্ষায়, আপনি একটি TestApplication ব্যবহার করবেন যা একটি ApplicationComponent এর পরিবর্তে TestApplicationComponent তৈরি করে।

কোটলিন

// Your test application needs an instance of the test graph
class MyTestApplication: MyApplication() {
    override val appComponent = DaggerTestApplicationComponent.create()
}

জাভা

// Your test application needs an instance of the test graph
public class MyTestApplication extends MyApplication {
    ApplicationComponent appComponent = DaggerTestApplicationComponent.create();
}

তারপর, এই পরীক্ষা অ্যাপ্লিকেশনটি একটি কাস্টম TestRunner এ ব্যবহৃত হয় যা আপনি ইন্সট্রুমেন্টেশন পরীক্ষা চালানোর জন্য ব্যবহার করবেন। এই সম্পর্কে আরও তথ্যের জন্য, আপনার অ্যান্ড্রয়েড অ্যাপ কোডল্যাবে ড্যাগার ব্যবহার করে দেখুন।

ড্যাগার মডিউল নিয়ে কাজ করা

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

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

ভাল অনুশীলন নির্দেশ করে যে মডিউলগুলি শুধুমাত্র একটি উপাদানে একবার ঘোষণা করা উচিত (নির্দিষ্ট উন্নত ড্যাগার ব্যবহারের ক্ষেত্রের বাইরে)।

ধরা যাক আপনি এইভাবে আপনার গ্রাফ কনফিগার করেছেন। ApplicationComponent মধ্যে রয়েছে Module1 এবং Module2 এবং Module1 মধ্যে ModuleX রয়েছে।

কোটলিন

@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module
class Module2 { ... }

জাভা

@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = {ModuleX.class})
public class Module1 { ... }

@Module
public class Module2 { ... }

যদি এখন Module2 ModuleX দ্বারা প্রদত্ত ক্লাসের উপর নির্ভর করে। একটি খারাপ অনুশীলন হল Module2 তে ModuleX অন্তর্ভুক্ত কারণ নিম্নোক্ত কোড স্নিপেটে দেখানো গ্রাফে ModuleX দুবার অন্তর্ভুক্ত করা হয়েছে:

কোটলিন

// Bad practice: ModuleX is declared multiple times in this Dagger graph
@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module(includes = [ModuleX::class])
class Module2 { ... }

জাভা

// Bad practice: ModuleX is declared multiple times in this Dagger graph.
@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = ModuleX.class)
public class Module1 { ... }

@Module(includes = ModuleX.class)
public class Module2 { ... }

পরিবর্তে, আপনার নিম্নলিখিতগুলির মধ্যে একটি করা উচিত:

  1. মডিউল রিফ্যাক্টর করুন এবং সাধারণ মডিউলটিকে কম্পোনেন্টে বের করুন।
  2. উভয় মডিউল শেয়ার করে এমন বস্তুর সাথে একটি নতুন মডিউল তৈরি করুন এবং এটিকে কম্পোনেন্টে বের করুন।

এইভাবে রিফ্যাক্টর না করার ফলে অনেকগুলি মডিউল একে অপরকে সহ সংগঠনের স্পষ্ট ধারণা ছাড়াই তৈরি করে এবং প্রতিটি নির্ভরতা কোথা থেকে আসছে তা দেখা আরও কঠিন করে তোলে।

ভাল অনুশীলন (বিকল্প 1) : মডিউলএক্স একবার ড্যাগার গ্রাফে ঘোষণা করা হয়।

কোটলিন

@Component(modules = [Module1::class, Module2::class, ModuleX::class])
interface ApplicationComponent { ... }

@Module
class Module1 { ... }

@Module
class Module2 { ... }

জাভা

@Component(modules = {Module1.class, Module2.class, ModuleX.class})
public interface ApplicationComponent { ... }

@Module
public class Module1 { ... }

@Module
public class Module2 { ... }

ভাল অনুশীলন (বিকল্প 2) : ModuleXModule1 এবং Module2 থেকে সাধারণ নির্ভরতাগুলি ModuleXCommon নামে একটি নতুন মডিউলে বের করা হয় যা উপাদানটিতে অন্তর্ভুক্ত করা হয়েছে। তারপর ModuleXWithModule1Dependencies এবং ModuleXWithModule2Dependencies নামে আরও দুটি মডিউল প্রতিটি মডিউলের জন্য নির্দিষ্ট নির্ভরতাগুলির সাথে তৈরি করা হয়। সমস্ত মডিউল ড্যাগার গ্রাফে একবার ঘোষণা করা হয়।

কোটলিন

@Component(modules = [Module1::class, Module2::class, ModuleXCommon::class])
interface ApplicationComponent { ... }

@Module
class ModuleXCommon { ... }

@Module
class ModuleXWithModule1SpecificDependencies { ... }

@Module
class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = [ModuleXWithModule1SpecificDependencies::class])
class Module1 { ... }

@Module(includes = [ModuleXWithModule2SpecificDependencies::class])
class Module2 { ... }

জাভা

@Component(modules = {Module1.class, Module2.class, ModuleXCommon.class})
public interface ApplicationComponent { ... }

@Module
public class ModuleXCommon { ... }

@Module
public class ModuleXWithModule1SpecificDependencies { ... }

@Module
public class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = ModuleXWithModule1SpecificDependencies.class)
public class Module1 { ... }

@Module(includes = ModuleXWithModule2SpecificDependencies.class)
public class Module2 { ... }

সহায়ক ইনজেকশন

অ্যাসিস্টেড ইনজেকশন হল একটি ডিআই প্যাটার্ন যা একটি অবজেক্ট তৈরি করতে ব্যবহৃত হয় যেখানে কিছু প্যারামিটার ডিআই ফ্রেমওয়ার্ক দ্বারা সরবরাহ করা হতে পারে এবং অন্যগুলি তৈরির সময় ব্যবহারকারীকে অবশ্যই পাস করতে হবে।

অ্যান্ড্রয়েডে, এই প্যাটার্নটি বিশদ স্ক্রিনে সাধারণ যেখানে দেখানো উপাদানটির আইডি শুধুমাত্র রানটাইমে জানা যায়, কম্পাইলের সময় নয় যখন ড্যাগার DI গ্রাফ তৈরি করে। ড্যাগারের সাহায্যে ইনজেকশন সম্পর্কে আরও জানতে, ড্যাগার ডকুমেন্টেশন দেখুন।

উপসংহার

যদি আপনি ইতিমধ্যেই না করে থাকেন তবে সেরা অনুশীলন বিভাগটি পর্যালোচনা করুন। একটি অ্যান্ড্রয়েড অ্যাপে ড্যাগার কীভাবে ব্যবহার করবেন তা দেখতে, একটি অ্যান্ড্রয়েড অ্যাপ কোডল্যাবে ড্যাগার ব্যবহার করুন দেখুন।