অ্যান্ড্রয়েড ভিউমোডেল অতিরিক্ত যুক্তি


116

AndroidViewModelঅ্যাপ্লিকেশন প্রসঙ্গ ব্যতীত আমার কাস্টম নির্মাতার কাছে অতিরিক্ত যুক্তি পাঠানোর কোনও উপায় আছে কি ? উদাহরণ:

public class MyViewModel extends AndroidViewModel {
    private final LiveData<List<MyObject>> myObjectList;
    private AppDatabase appDatabase;

    public MyViewModel(Application application, String param) {
        super(application);
        appDatabase = AppDatabase.getDatabase(this.getApplication());

        myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
    }
}

এবং যখন আমি আমার কাস্টম ViewModelক্লাসটি ব্যবহার করতে চাই তখন আমি এই কোডটি আমার খণ্ডে ব্যবহার করি:

MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)

সুতরাং আমি কীভাবে String paramআমার কাস্টমগুলিতে অতিরিক্ত যুক্তিটি পাস করব জানি না ViewModel। আমি কেবল অ্যাপ্লিকেশন প্রসঙ্গটি পাস করতে পারি, তবে অতিরিক্ত যুক্তিগুলি না। আমি যেকোন সাহায্য এর জন্য সত্যিই কৃতজ্ঞ থাকব. ধন্যবাদ.

সম্পাদনা: আমি কিছু কোড যুক্ত করেছি। আমি আশা করি এটি এখন আরও ভাল।


আরও বিশদ এবং কোড যুক্ত করুন
হুগো

ত্রুটির বার্তাটি কী?
মূসা অ্যাপ্রিকো

কোনও ত্রুটির বার্তা নেই। আমি সহজেই জানি না যে কনস্ট্রাক্টরের পক্ষে যুক্তিগুলি কোথায় সেট করা যায় কারণ ভিউমোডেলপ্রোভাইডারটি অ্যান্ড্রয়েডভিউমোডেল অবজেক্ট তৈরি করার জন্য ব্যবহৃত হয়।
মারিও রুডম্যান

উত্তর:


225

আপনার ভিউমোডেলের জন্য আপনার একটি কারখানার শ্রেণি থাকা দরকার।

public class MyViewModelFactory implements ViewModelProvider.Factory {
    private Application mApplication;
    private String mParam;


    public MyViewModelFactory(Application application, String param) {
        mApplication = application;
        mParam = param;
    }


    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        return (T) new MyViewModel(mApplication, mParam);
    }
}

এবং ভিউ মডেলটি ইনস্ট্যান্ট করার সময় আপনি এটি পছন্দ করেন:

MyViewModel myViewModel = ViewModelProvider(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);

কোটলিনের জন্য, আপনি প্রতিনিধি সম্পত্তি ব্যবহার করতে পারেন:

val viewModel: MyViewModel by viewModels { MyViewModelFactory(getApplication(), "my awesome param") }

আরও একটি নতুন বিকল্প রয়েছে - আপনার কারখানার ইনস্ট্যান্টেশন প্রয়োগ HasDefaultViewModelProviderFactoryও প্রয়োগ getDefaultViewModelProviderFactory()করতে এবং তারপরে আপনি কল করতে ViewModelProvider(this)বা by viewModels()কারখানা ছাড়াই।


5
প্রত্যেক ViewModelশ্রেণীর কি তার ভিউমোডেলফ্যাক্টরির প্রয়োজন?
dmlebron

7
তবে প্রত্যেকের ViewModel/ আলাদা আলাদা ডিআই থাকতে পারে। আপনি কীভাবে জানবেন কোন create()পদ্ধতিটি পদ্ধতিতে ফিরে আসে ?
dmlebron

4
আপনার ভিউমোডেলটি পরিবর্তন ওরিয়েন্টেশনের পরে পুনরায় তৈরি করা হবে। আপনার পোষাক প্রতিবার কারখানা তৈরি করে।
টিম

4
এটা সত্যি না. নতুন ViewModelসৃষ্টি পদ্ধতি প্রতিরোধ করে get()। ডকুমেন্টেশনের উপর ভিত্তি করে: "এই ভিউমোডেলপ্রোভাইডারের সাথে যুক্ত একটি বিদ্যমান ভিউমোডেল ফিরিয়ে দেয় বা সুযোগে একটি নতুন তৈরি করে (সাধারণত, একটি খণ্ড বা কোনও ক্রিয়াকলাপ)" দেখুন: developer.android.com/references/android/arch/lifecycle/…
mlyko

4
return modelClass.cast(new MyViewModel(mApplication, mParam))সতর্কতা থেকে মুক্তি পেতে কীভাবে ব্যবহার করবেন
জ্যাকাইকফ্লাউ 21:58

25

নির্ভরতা ইনজেকশন সহ কার্যকর করুন

এটি উত্পাদন কোডের জন্য আরও উন্নত এবং উন্নত better

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

ড্যাগার হিল্ট , পরবর্তী প্রজন্মের সমাধান, আলফাতে 7/12/20 হিসাবে লাইব্রেরি প্রকাশের স্থিতিতে আসার পরে একটি সহজ সেটআপ সহ একই ব্যবহারের ক্ষেত্রে প্রস্তাব দেয়।

সঙ্গে বাস্তবায়ন লাইফ সাইকল 2.2.0 Kotlin মধ্যে

পাসিং আর্গুমেন্ট / পরামিতি

// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
} 

class SomeViewModel(private val someString: String) : ViewModel() {
    init {
        //TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
    }
}

class Fragment: Fragment() {
    // Create VM in activity/fragment with VM factory.
    val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") } 
}

আর্গুমেন্ট / পরামিতিগুলির সাহায্যে সেভডস্টেট সক্ষম করা

class SomeViewModelFactory(
        private val owner: SavedStateRegistryOwner,
        private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
    override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
            SomeViewModel(state, someString) as T
}

class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
    val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
        if (position == null) 0 else position
    }
        
    init {
        //TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
    }
        
     fun saveFeedPosition(position: Int) {
        state.set(FEED_POSITION_KEY, position)
    }
}

class Fragment: Fragment() {
    // Create VM in activity/fragment with VM factory.
    val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") } 
    private var feedPosition: Int = 0
     
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
                .findFirstVisibleItemPosition())
    }    
        
    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        feedPosition = someViewModel.feedPosition
    }
}

কারখানায় তৈরির কাজটি ওভাররাইড করার সময় আমি একটি সতর্কতা পেয়েছি যাচাই করা না করে 'আইটেমভিউ মডেল টু টি'
সেনেওনজো

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

15

একাধিক ভিন্ন ভিউ মডেলের মধ্যে ভাগ করা একটি কারখানার জন্য আমি ম্লিকোর উত্তরটি এভাবে প্রসারিত করব:

public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    private Application mApplication;
    private Object[] mParams;

    public MyViewModelFactory(Application application, Object... params) {
        mApplication = application;
        mParams = params;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (modelClass == ViewModel1.class) {
            return (T) new ViewModel1(mApplication, (String) mParams[0]);
        } else if (modelClass == ViewModel2.class) {
            return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
        } else if (modelClass == ViewModel3.class) {
            return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
        } else {
            return super.create(modelClass);
        }
    }
}

এবং ভিউ মডেলগুলি ইনস্ট্যান্ট করছে:

ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);

বিভিন্ন ভিউ মডেলের সাথে বিভিন্ন কনস্ট্রাক্টর রয়েছে।


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

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

4

অ্যানড্রইডভিউমোডেল ক্ষেত্রে উপরের কোটলিন সমাধান @ vilpe89 এর উপর ভিত্তি করে

class ExtraParamsViewModelFactory(private val application: Application, private val myExtraParam: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(application, myExtraParam) as T

}

তারপরে কোনও খণ্ডটি ভিউমোডেল হিসাবে শুরু করতে পারে

class SomeFragment : Fragment() {
 ....
    private val myViewModel: SomeViewModel by viewModels {
        ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")
    }
 ....
}

এবং তারপরে আসল ভিউমোডেল ক্লাস

class SomeViewModel(application: Application, val myExtraParam:String) : AndroidViewModel(application) {
....
}

বা কিছু উপযুক্ত পদ্ধতিতে ...

override fun onActivityCreated(...){
    ....

    val myViewModel = ViewModelProvider(this, ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")).get(SomeViewModel::class.java)

    ....
}

প্রশ্নটি জিজ্ঞাসা করে যে উপরোক্ত অনুসরণ করে না এমন প্রসঙ্গটি ব্যবহার না করে কীভাবে আর্গুমেন্ট / প্যারামিটারগুলি পাস করবেন: অ্যাপ্লিকেশন প্রসঙ্গ ব্যতীত আমার কাস্টম অ্যান্ড্রয়েডভিডমোডেল কনস্ট্রাক্টরের অতিরিক্ত যুক্তি দেওয়ার কোনও উপায় আছে কি?
অ্যাডাম হুরউইটজ

3

আমি এটিকে একটি শ্রেণিতে তৈরি করেছি যেখানে ইতিমধ্যে তৈরি করা বস্তুটি পাস হয়ে গেছে।

private Map<String, ViewModel> viewModelMap;

public ViewModelFactory() {
    this.viewModelMap = new HashMap<>();
}

public void add(ViewModel viewModel) {
    viewModelMap.put(viewModel.getClass().getCanonicalName(), viewModel);
}

@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
        if (viewModel.getKey().equals(modelClass.getCanonicalName())) {
            return (T) viewModel.getValue();
        }
    }
    return null;
}

এবং তারপর

ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.add(new SampleViewModel(arg1, arg2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);

কনস্ট্রাক্টরের প্যারামিটারগুলি পাস করার জন্য প্রতিটি ভিউমোডেলের জন্য আমাদের ভিউমোডেলফ্যাক্টরি থাকা উচিত ??
কে প্রদীপ কুমার রেড্ডি

সব ViewModels নং কেবলমাত্র একটি ViewModelFactory
Danil

হ্যাশম্যাপ কী হিসাবে ক্যানোনিকাল নামটি ব্যবহার করার কোনও কারণ আছে? আমি কি ক্লাস.সিম্পলনাম ব্যবহার করতে পারি?
কে প্রদীপ কুমার রেড্ডি

হ্যাঁ, তবে আপনাকে অবশ্যই নিশ্চিত করতে হবে যে কোনও সদৃশ নাম নেই
ড্যানিল

এটি কি কোড লেখার প্রস্তাবিত স্টাইল? আপনি নিজের থেকে এই কোডটি নিয়ে এসেছেন বা আপনি এন্ড্রয়েড ডক্সে পড়েছেন?
কে প্রদীপ কুমার রেড্ডি

1

আমি একটি লাইব্রেরি লিখেছি যা এই আরও সোজাসাপ্টা এবং ওয়ে ক্লিনার তৈরি করা উচিত, ভিউমোডেল আর্গুমেন্টগুলির সাথে নির্বিঘ্নে কাজ করার সময় ডাগার দ্বারা নির্ভরতা হিসাবে সরবরাহ করা যেতে পারে: https://github.com/radutopor/ViewModelFactory

@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {

    val greeting = MutableLiveData<String>()

    init {
        val user = repository.getUser(userId)
        greeting.value = "Hello, $user.name"
    }    
}

দেখুন মধ্যে:

class UserActivity : AppCompatActivity() {
    @Inject
    lateinit var userViewModelFactory2: UserViewModelFactory2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        appComponent.inject(this)

        val userId = intent.getIntExtra("USER_ID", -1)
        val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
            .get(UserViewModel::class.java)

        viewModel.greeting.observe(this, Observer { greetingText ->
            greetingTextView.text = greetingText
        })
    }
}

1

(কোটলিন) আমার দ্রবণটিতে প্রতিবিম্বের কিছুটা ব্যবহার করা হয়।

আসুন আপনাকে বলুন যে প্রতিবার নতুন ভিউমোডেল ক্লাস তৈরি করার সময় আপনি একই ধরণের ফ্যাক্টরি ক্লাস তৈরি করতে চান না যার জন্য কিছু যুক্তি প্রয়োজন। আপনি প্রতিচ্ছবি মাধ্যমে এটি সম্পাদন করতে পারেন।

উদাহরণস্বরূপ আপনার দুটি আলাদা ক্রিয়াকলাপ হবে:

class Activity1 : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
        val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
            .get(ViewModel1::class.java)
    }
}

class Activity2 : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val args = Bundle().apply { putInt("AGE_KEY", 29) }
        val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
            .get(ViewModel2::class.java)
    }
}

এবং এই ক্রিয়াকলাপগুলির জন্য ভিউমোডেলস:

class ViewModel1(private val args: Bundle) : ViewModel()

class ViewModel2(private val args: Bundle) : ViewModel()

তারপরে যাদু অংশ, কারখানা শ্রেণীর প্রয়োগ:

class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        try {
            val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.java)
            return constructor.newInstance(args)
        } catch (e: Exception) {
            Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
            throw e
        }
    }
}

1
class UserViewModelFactory(private val context: Context) : ViewModelProvider.NewInstanceFactory() {
 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return UserViewModel(context) as T
    }
 
}
class UserViewModel(private val context: Context) : ViewModel() {
 
    private var listData = MutableLiveData<ArrayList<User>>()
 
    init{
        val userRepository : UserRepository by lazy {
            UserRepository
        }
        if(context.isInternetAvailable()) {
            listData = userRepository.getMutableLiveData(context)
        }
    }
 
    fun getData() : MutableLiveData<ArrayList<User>>{
        return listData
    }

ক্রিয়াকলাপে ভিউ মডেলকে কল করুন

val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)

আরও রেফারেন্সের জন্য: অ্যান্ড্রয়েড এমভিভিএম কোটলিন উদাহরণ


প্রশ্নটি জিজ্ঞাসা করে যে উপরোক্ত অনুসরণ করে না এমন প্রসঙ্গটি ব্যবহার না করে কীভাবে আর্গুমেন্ট / প্যারামিটারগুলি পাস করবেন: অ্যাপ্লিকেশন প্রসঙ্গ ব্যতীত আমার কাস্টম অ্যান্ড্রয়েডভিডমোডেল কনস্ট্রাক্টরের অতিরিক্ত যুক্তি দেওয়ার কোনও উপায় আছে কি?
অ্যাডাম হুরউইটজ

আপনি আপনার কাস্টম ভিউ মডেল কনস্ট্রাক্টরের যেকোন যুক্তি / প্যারামিটারটি পাস করতে পারেন। এখানে প্রসঙ্গটি কেবল একটি উদাহরণ। আপনি কনস্ট্রাক্টারে কোনও কাস্টম আর্গুমেন্ট পাস করতে পারেন।
ধ্রুমিল শাহ

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

0

এটি কেন এমন করবেন না:

public class MyViewModel extends AndroidViewModel {
    private final LiveData<List<MyObject>> myObjectList;
    private AppDatabase appDatabase;
    private boolean initialized = false;

    public MyViewModel(Application application) {
        super(application);
    }

    public initialize(String param){
      synchronized ("justInCase") {
         if(! initialized){
          initialized = true;
          appDatabase = AppDatabase.getDatabase(this.getApplication());
          myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
    }
   }
  }
}

এবং তারপরে এটি দুটি ধাপে এটি ব্যবহার করুন:

MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
myViewModel.initialize(param)

4
কনস্ট্রাক্টরে প্যারামিটার স্থাপনের পুরো বিন্দুটি কেবল একবার ভিউ মডেলকে আরম্ভ করা । আপনার বাস্তবায়নের সাথে, যদি আপনি ক্রিয়াকলাপটি কল myViewModel.initialize(param)করেন onCreate, উদাহরণস্বরূপ, এটি MyViewModelব্যবহারকারীকে ডিভাইসটি ঘোরানোর সাথে সাথে একই উদাহরণে একাধিকবার বলা যেতে পারে ।
সানলোক লি

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