ভিউপ্যাজারের ভিতরে একই বিভাগগুলির ভিউমোডেল ইনজেক্ট করতে ডাগার 2 কীভাবে ব্যবহার করবেন


10

আমি আমার প্রকল্পে ডগার 2 যুক্ত করার চেষ্টা করছি। আমি আমার খণ্ডগুলির জন্য ভিউমোডেলগুলি (অ্যান্ড্রয়েডএক্স আর্কিটেকচার উপাদান) ইনজেক্ট করতে সক্ষম হয়েছি।

আমার কাছে একটি ভিউপিজার রয়েছে যার একই টুকরোটির 2 টি উদাহরণ রয়েছে (প্রতিটি ট্যাবগুলির জন্য কেবলমাত্র একটি ছোট্ট পরিবর্তন) এবং প্রতিটি ট্যাবে আমি LiveDataডেটা পরিবর্তনে আপডেট হওয়া (এপিআই থেকে) পর্যবেক্ষণ করছি ।

সমস্যাটি হ'ল এপিআই প্রতিক্রিয়াটি আসে এবং আপডেট করে LiveData, বর্তমানে দৃশ্যমান খণ্ডে একই তথ্য সমস্ত ট্যাবে পর্যবেক্ষকদের কাছে প্রেরণ করা হচ্ছে। (আমি মনে করি এটি সম্ভবত স্কোপ এর কারণে হয়েছে ViewModel)।

এইভাবে আমি আমার ডেটা পর্যবেক্ষণ করছি:

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        activityViewModel.expenseList.observe(this, Observer {
            swipeToRefreshLayout.isRefreshing = false
            viewAdapter.setData(it)
        })
    ....
}

আমি এই ক্লাসটি সরবরাহ করার জন্য ব্যবহার করছি ViewModel:

class ViewModelProviderFactory @Inject constructor(creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>?) :
    ViewModelProvider.Factory {
    private val creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>? = creators
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel?>? = creators!![modelClass]
        if (creator == null) { // if the viewmodel has not been created
// loop through the allowable keys (aka allowed classes with the @ViewModelKey)
            for (entry in creators.entries) { // if it's allowed, set the Provider<ViewModel>
                if (modelClass.isAssignableFrom(entry.key!!)) {
                    creator = entry.value
                    break
                }
            }
        }
        // if this is not one of the allowed keys, throw exception
        requireNotNull(creator) { "unknown model class $modelClass" }
        // return the Provider
        return try {
            creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }

    companion object {
        private val TAG: String? = "ViewModelProviderFactor"
    }
}

আমি আমার এইভাবে আবদ্ধ করছি ViewModel:

@Module
abstract class ActivityViewModelModule {
    @MainScope
    @Binds
    @IntoMap
    @ViewModelKey(ActivityViewModel::class)
    abstract fun bindActivityViewModel(viewModel: ActivityViewModel): ViewModel
}

আমি @ContributesAndroidInjectorএইভাবে আমার খণ্ডের জন্য ব্যবহার করছি:

@Module
abstract class MainFragmentBuildersModule {

    @ContributesAndroidInjector
    abstract fun contributeActivityFragment(): ActivityFragment
}

এবং আমি এই মডিউলগুলি আমার MainActivityউপ-সংস্থায় এটি যুক্ত করছি:

@Module
abstract class ActivityBuilderModule {
...
    @ContributesAndroidInjector(
        modules = [MainViewModelModule::class, ActivityViewModelModule::class,
            AuthModule::class, MainFragmentBuildersModule::class]
    )
    abstract fun contributeMainActivity(): MainActivity
}

এখানে আমার AppComponent:

@Singleton
@Component(
    modules =
    [AndroidSupportInjectionModule::class,
        ActivityBuilderModule::class,
        ViewModelFactoryModule::class,
        AppModule::class]
)
interface AppComponent : AndroidInjector<SpenmoApplication> {

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }
}

আমি এর মতো প্রসারিত DaggerFragmentএবং ইনজেকশন দিচ্ছি ViewModelProviderFactory:

@Inject
lateinit var viewModelFactory: ViewModelProviderFactory

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
....
activityViewModel =
            ViewModelProviders.of(this, viewModelFactory).get(key, ActivityViewModel::class.java)
        activityViewModel.restartFetch(hasReceipt)
}

keyউভয় টুকরা ভাষার জন্য আলাদা হতে হবে।

আমি কীভাবে নিশ্চিত করতে পারি যে কেবলমাত্র বর্তমান খণ্ডের পর্যবেক্ষক আপডেট হচ্ছে।

সম্পাদনা 1 ->

আমি ত্রুটি সহ একটি নমুনা প্রকল্প যুক্ত করেছি। দেখে মনে হচ্ছে সমস্যাটি কেবল তখনই ঘটে যখন একটি কাস্টম স্কোপ যুক্ত করা হয়। দয়া করে এখানে নমুনা প্রকল্পটি দেখুন: গিথুব লিঙ্ক

masterশাখাটি ইস্যু সহ অ্যাপ্লিকেশন আছে। আপনি যদি কোনও ট্যাব রিফ্রেশ করেন (রিফ্রেশ করতে সোয়াইপ করুন) তবে আপডেট হওয়া মানটি দুটি ট্যাবেই প্রতিবিম্বিত হচ্ছে। এটি কেবল তখনই ঘটে যখন আমি এটিতে একটি কাস্টম সুযোগ যুক্ত করি ( @MainScope)।

working_fine শাখায় একই অ্যাপ্লিকেশন রয়েছে যার কোনও কাস্টম সুযোগ নেই এবং এটির কাজ ঠিক আছে।

প্রশ্নটি পরিষ্কার না হলে দয়া করে আমাকে জানান।


আমি পাচ্ছি না কেন আপনি working_fineশাখা থেকে পদ্ধতির ব্যবহার করবেন না ? আপনার সুযোগ কেন দরকার?
আজিজবিকিয়ান

@azizbekian আমি বর্তমানে কার্যকরী সূক্ষ্ম শাখা ব্যবহার করছি .. তবে আমি জানতে চাই, কেন স্কোপ ব্যবহার করে এটি ভাঙবে।
3x_voice

উত্তর:


1

আমি আসল প্রশ্নটি পুনরুদ্ধার করতে চাই, এটি এখানে:

আমি বর্তমানে কাজটি ব্যবহার করছি fine_branch, তবে আমি জানতে চাই, স্কোপ ব্যবহার করে এটি কেন ভাঙবে।

আমার বোঝার অনুসারে আপনার মনে এই ধারণাটি রয়েছে যে আপনি ViewModelবিভিন্ন কী ব্যবহারের উদাহরণ গ্রহণ করার চেষ্টা করছেন , তবে আপনাকে বিভিন্ন উদাহরণ প্রদান করা উচিত ViewModel:

// in first fragment
ViewModelProvider(...).get("true", PagerItemViewModel::class.java)

// in second fragment
ViewModelProvider(...).get("false", PagerItemViewModel::class.java)

বাস্তবতা কিছুটা আলাদা। আপনি যদি খণ্ডে নিম্নলিখিত লগ রাখেন তবে আপনি দেখতে পাবেন যে সেই দুটি টুকরোগুলি ঠিক একই উদাহরণ ব্যবহার করছে PagerItemViewModel:

Log.i("vvv", "${if (oneOrTwo) "one:" else "two:"} viewModel hash is ${viewModel.hashCode()}")

আসুন ডুব দেই এবং বুঝতে পারি যে এটি কেন ঘটে।

অভ্যন্তরীণভাবে মূলত টু মানচিত্র যা থেকে একটি ViewModelProvider#get()উদাহরণ পাওয়ার চেষ্টা করবে ।PagerItemViewModelViewModelStoreStringViewModel

যখন FirstFragmentকোনও উদাহরণটি জিজ্ঞাসা করা PagerItemViewModelহয় mapএটি খালি, সুতরাং mFactory.create(modelClass)মৃত্যুদন্ড কার্যকর করা হয় যা শেষ হয় ViewModelProviderFactory। নিম্নলিখিত কোড সহ creator.get()কলিং শেষ হয় DoubleCheck:

  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) { // 1
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          instance = reentrantCheck(instance, result); // 2
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }

instanceএখন null, অত একটি নতুন দৃষ্টান্ত PagerItemViewModelসৃষ্টি করা হয় এবং সংরক্ষিত হয় instance(দেখুন // 2)।

এখন ঠিক একই পদ্ধতিটি এর জন্য ঘটে SecondFragment:

  • খণ্ড একটি উদাহরণ জিজ্ঞাসা PagerItemViewModel
  • mapএখন না খালি, কিন্তু আছে না একটি দৃষ্টান্ত ধারণ PagerItemViewModelকী দিয়েfalse
  • এর একটি নতুন উদাহরণ তৈরি PagerItemViewModelকরা শুরু করা হয়mFactory.create(modelClass)
  • ভিতরে ViewModelProviderFactoryবাস্তবায়ন পৌঁছে যায় creator.get()যার বাস্তবায়নDoubleCheck

এখন, মূল মুহূর্ত। এই DoubleCheckহল একই উদাহরণ হিসেবে বলা যায় এর DoubleCheckযে তৈরি করার জন্য ব্যবহার করা হয়েছিল ViewModelউদাহরণস্বরূপ যখন FirstFragmentএটি জন্য জিজ্ঞাসা। কেন এটি একই উদাহরণ? কারণ আপনি সরবরাহকারী পদ্ধতিতে একটি সুযোগ প্রয়োগ করেছেন।

if (result == UNINITIALIZED)(// 1) মিথ্যা এবং সঠিক একই ক্ষেত্রটিকেই নির্ণয় করা হয় ViewModelআহ্বানকারী ফিরে হচ্ছে - SecondFragment

এখন, উভয় টুকরা একই উদাহরণ ব্যবহার করছে ViewModelতাই এটি সম্পূর্ণরূপে ঠিক যে তারা একই ডেটা প্রদর্শন করছে।


উত্তরের জন্য ধন্যবাদ. এইবার বুঝতে পারছি. তবে সুযোগ ব্যবহার করার সময় এটি ঠিক করার কোনও উপায় নেই?
3x_voice

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

আমি ভুল হতে পারে। আমার প্রত্যাশা ছিল স্কোপ ব্যবহার করা আরও ভাল পদ্ধতির। যেমন যেমন যদি আমার অ্যাপ্লিকেশনটিতে 2 টি ক্রিয়াকলাপ (লগইন এবং প্রধান) থাকে তবে লগইন করার জন্য 1 টি কাস্টম স্কোপ এবং
প্রধানটির

> আমার প্রত্যাশা ছিল স্কোপ ব্যবহার করা একটি ভাল পদ্ধতির এটি এটি অন্যটির চেয়ে ভাল নয়। তারা বিভিন্ন সমস্যা সমাধান করছে, প্রত্যেকের ব্যবহারের ক্ষেত্রে রয়েছে।
আজিজিবিয়ান

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

0

উভয় টুকরা লাইভেটা থেকে আপডেটটি গ্রহণ করে কারণ ভিউজিয়ার উভয় টুকরো পুনরায় শুরু অবস্থায় রেখে দেয়। যেহেতু আপনি শুধুমাত্র বর্তমান টুকরা viewpager দৃশ্যমান আপডেট প্রয়োজন, প্রেক্ষাপটে বর্তমান টুকরা, হোস্ট কার্যকলাপ দ্বারা সংজ্ঞায়িত করা হয় কার্যকলাপ উচিত আকাঙ্ক্ষিত টুকরা স্পষ্টভাবে সরাসরি আপডেট।

ভিউপেজারে যুক্ত হওয়া সমস্ত খণ্ডের জন্য লাইভডাটাতে প্রবেশের জন্য ফ্রেগমেন্টের একটি মানচিত্র বজায় রাখতে হবে (একই সনাক্তকারীর সাথে নিশ্চিত হওয়া উচিত যা একই খণ্ডের দুটি অংশের পৃথক পার্থক্য করতে পারে)।

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

কোড ইমপ্লের মতো দেখতে হবে -

class Activity {
    val mapOfFragmentToLiveData<FragmentId, MutableLiveData> = mutableMapOf<>()

    val mediatorLiveData : MediatorLiveData<OriginalData> = object : MediatorLiveData() {
        override fun onChanged(newData : OriginalData) {
           // here get the livedata observed by the  currently selected fragment
           val currentSelectedFragmentLiveData = mapOfFragmentToLiveData.get(viewpager.getSelectedItem())
          // now post the update on this livedata
           currentSelectedFragmentLiveData.value = newData
        }
    }

  fun getOriginalLiveData(fragment : YourFragment) : LiveData<OriginalData> {
     return mapOfFragmentToLiveData.get(fragment) ?: MutableLiveData<OriginalData>().run {
       mapOfFragmentToLiveData.put(fragment, this)
  }
} 

class YourFragment {
    override fun onActivityCreated(bundle : Bundle){
       //get activity and request a livedata 
       getActivity().getOriginalLiveData(this).observe(this, Observer { _newData ->
           // observe here 
})
    }
}

উত্তরের জন্য ধন্যবাদ. আমি ব্যবহার করছি FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)তাই কীভাবে ভিউজিয়ার উভয় টুকরো পুনরায় শুরু অবস্থায় রেখেছেন? আমি প্রকল্পে ডগার 2 যুক্ত করার আগে এটি ঘটছিল না।
3x_voice

আমি চেষ্টা করব এবং
বর্ণিত

আরে আমি একটি নমুনা প্রকল্প যুক্ত করেছি। আপনি এটি পরীক্ষা করতে পারেন। আমি
এটির

@ ধাক্কা_ভয়েস শিওর আপনার কাছে ফিরে আসবে।
বিশাল অরোরা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.