Different inline extension functions to get viewmodel

MavericksViewModel is a class for MVI development that manages and updates MavericksState and provides a state stream for other classes to subscribe to.
function similarities and differences scenes to be used advantage shortcoming
fragmentViewModel Used to get or create a new MavericksViewModel instance in Fragment , the life cycle of this instance is the same as Fragment . The case where each Fragment needs a separate ViewModel. Data confusion or leakage of ViewModel can be avoided. Data and state cannot be shared with other Fragments.
parentFragmentViewModel Used to obtain or create a MavericksViewModel instance shared with the parent Fragment in the Fragment . The life cycle of this instance is the same as that of the parent Fragment . The case where the child Fragment and the parent Fragment need to share data and state. Data transfer and updates can be simplified. It is necessary to traverse all parent Fragments to find or create ViewModel instances, which may affect performance.
targetFragmentViewModel Used to obtain or create a MavericksViewModel instance shared with the target Fragment (by setting) in the Fragment . The life cycle of this instance is the same as that of the target Fragment .setTargetFragment A situation where two unrelated Fragments need to share data and state. The target Fragment can be specified flexibly. It is necessary to handle the abnormal situation that the target Fragment does not exist or does not have a corresponding ViewModel.
existingViewModel It is used to obtain an existing MavericksViewModel instance in Fragment , and the life cycle of this instance is the same as that of Activity . Pages in the middle of the process cannot be used as the entry point of the process. It is guaranteed to get the correct ViewModel instance. It is necessary to handle the abnormal situation that the corresponding ViewModel is not found.
viewModel Used to get or create a new MavericksViewModel instance in the Activity , the life cycle of this instance is the same as the Activity . Any use of ViewModel in Activity. You can easily initialize and obtain ViewModel instances. It may cause the data of ViewModel to be too large or inconsistent.

fragmentViewModelfunction

There are three generic type parameters:

  • T: FragmentSubclasses of , must also implement MavericksViewthe interface.
  • VM: MavericksViewModelA subclass of to specify the type of ViewModel to get.
  • S: MavericksStateA subclass of to specify the state type of the ViewModel.

fragmentViewModelThe function has two optional parameters:

  • viewModelClass: The class object used to specify the ViewModel, the default is VM::class.
  • keyFactory: The unique identifier used to generate ViewModel, the default is viewModelClass.java.name.

fragmentViewModelThe return value of the function is a MavericksDelegateProvider, which is an interface for providing a lazy delegate to get or create a ViewModel. fragmentViewModelThe function internally calls the viewModelDelegateProvider function, passing in viewModelClass, keyFactory, existingViewModel = false (indicating that the existing ViewModel is not used), and a lambda expression for creating a new ViewModel instance. This lambda expression uses the MavericksViewModelProvider.get function, passing viewModelClass, stateClass, viewModelContext (including activity and args), key, and initialStateFactory (used to initialize the state).

Fragment can get or create a new MavericksViewModel instance through a lazy delegate, and can specify the types of ViewModel and State as well as keyFactory.

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.fragmentViewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): MavericksDelegateProvider<T, VM> where T : Fragment, T : MavericksView =
    viewModelDelegateProvider(
        viewModelClass,
        keyFactory,
        existingViewModel = false
    ) {
    
     stateFactory ->
        MavericksViewModelProvider.get(
            viewModelClass = viewModelClass.java,
            stateClass = S::class.java,
            viewModelContext = FragmentViewModelContext(
                activity = requireActivity(),
                args = _fragmentArgsProvider(),
                fragment = this
            ),
            key = keyFactory(),
            initialStateFactory = stateFactory
        )
    }

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.parentFragmentViewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): MavericksDelegateProvider<T, VM> where T : Fragment, T : MavericksView =
    viewModelDelegateProvider(
        viewModelClass,
        keyFactory,
        existingViewModel = true 
        // 'existingViewModel'设置为true。虽然这个函数在两种情况下都能工作,
       //即已存在或新建的viewmodel,但是支持两种情况会比较困难,所以我们只测试常见的情况,
        //即“已存在”。我们不能确定这个fragment是否被设计为在非已存在的情况下使用(比如它可能需要参数)
        
    ) {
    
     stateFactory ->
        if (parentFragment == null) {
    
    
            // 使用ViewModelDoesNotExistException,这样模拟框架可以拦截并模拟这种情况下的viewmodel。
            throw ViewModelDoesNotExistException(
                "There is no parent fragment for ${this::class.java.name} so view model ${viewModelClass.java.name} could not be found." // ${this::class.java.name}没有父fragment,所以找不到view model ${viewModelClass.java.name}。
            )
        }
        var parent: Fragment? = parentFragment
        val key = keyFactory()
        while (parent != null) {
    
    
            try {
    
    
                return@viewModelDelegateProvider MavericksViewModelProvider.get(
                    viewModelClass = viewModelClass.java,
                    stateClass = S::class.java,
                    viewModelContext = FragmentViewModelContext(
                        activity = this.requireActivity(),
                        args = _fragmentArgsProvider(),
                        fragment = parent
                    ),
                    key = key,
                    forExistingViewModel = true 
                    // 对于已存在的viewmodel,设置为true。
                )
            } catch (e: ViewModelDoesNotExistException) {
    
    
                parent = parent.parentFragment 
                // 如果在当前父fragment中找不到viewmodel,就继续向上遍历父fragment。
            }
        }

        // 没有找到viewmodel。在最顶层的父fragment中创建一个新的。
        var topParentFragment = parentFragment
        while (topParentFragment?.parentFragment != null) {
    
    
            topParentFragment = topParentFragment.parentFragment
        }
        val viewModelContext = FragmentViewModelContext(
            requireActivity(),
            _fragmentArgsProvider(),
            topParentFragment!!
        )

        MavericksViewModelProvider.get(
            viewModelClass = viewModelClass.java,
            stateClass = S::class.java,
            viewModelContext = viewModelContext,
            key = keyFactory(),
            initialStateFactory = stateFactory
        )
    }

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.targetFragmentViewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): MavericksDelegateProvider<T, VM> where T : Fragment, T : MavericksView =
    viewModelDelegateProvider(
        viewModelClass,
        keyFactory,
        existingViewModel = true
    ) {
    
     stateFactory ->

        @Suppress("DEPRECATION")
        val targetFragment =
            requireNotNull(targetFragment) {
    
     "There is no target fragment for ${this::class.java.name}!" }

        MavericksViewModelProvider.get(
            viewModelClass = viewModelClass.java,
            stateClass = S::class.java,
            viewModelContext = FragmentViewModelContext(
                activity = requireActivity(),
                args = targetFragment._fragmentArgsProvider(),
                fragment = targetFragment
            ),
            key = keyFactory(),
            initialStateFactory = stateFactory
        )
    }

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.existingViewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): MavericksDelegateProvider<T, VM> where T : Fragment, T : MavericksView =
    viewModelDelegateProvider(
        viewModelClass,
        keyFactory,
        existingViewModel = true
    ) {
    
     stateFactory ->

        MavericksViewModelProvider.get(
            viewModelClass = viewModelClass.java,
            stateClass = S::class.java,
            viewModelContext = ActivityViewModelContext(
                requireActivity(),
                _fragmentArgsProvider()
            ),
            key = keyFactory(),
            initialStateFactory = stateFactory,
            forExistingViewModel = true
        )
    }

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.activityViewModel(
    viewModelClass: KClass<VM> = VM::class,
    noinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): MavericksDelegateProvider<T, VM> where T : Fragment, T : MavericksView =
    viewModelDelegateProvider(
        viewModelClass,
        keyFactory,
        existingViewModel = false
    ) {
    
     stateFactory ->

        MavericksViewModelProvider.get(
            viewModelClass = viewModelClass.java,
            stateClass = S::class.java,
            viewModelContext = ActivityViewModelContext(
                activity = requireActivity(),
                args = _fragmentArgsProvider()
            ),
            key = keyFactory(),
            initialStateFactory = stateFactory
        )
    }

inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.viewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String = {
    
     viewModelClass.java.name }
): Lazy<VM> where T : ComponentActivity = lifecycleAwareLazy(this) {
    
    
    MavericksViewModelProvider.get(
        viewModelClass = viewModelClass.java,
        stateClass = S::class.java,
        viewModelContext = ActivityViewModelContext(this, intent.extras?.get(Mavericks.KEY_ARG)),
        key = keyFactory()
    )
}

Code example for each function, assuming there is an OrderViewModel and an OrderState for managing the data and state of the order.

- fragmentViewModel: To use this function in Fragment, you can write it like this:
class OrderFragment: Fragment(), MavericksView {
    
    

  // 获取或创建一个新的OrderViewModel实例
  private val viewModel: OrderViewModel by fragmentViewModel()

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    
    
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_order, container, false)
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
    
    super.onViewCreated(view, savedInstanceState)

    // 绑定视图和数据
    binding.viewModel = viewModel
    binding.lifecycleOwner = viewLifecycleOwner

    // 设置点击事件
    binding.buttonConfirm.setOnClickListener {
    
    
      viewModel.confirmOrder()
    }
  }

  override fun invalidate() {
    
    
    // 更新UI
    withState(viewModel) {
    
     state ->
      binding.textViewOrderStatus.text = state.orderStatus
      binding.textViewOrderPrice.text = state.orderPrice.toString()
    }
  }
}

- parentFragmentViewModel: To use this function in a child Fragment, you can write it like this:
class OrderDetailFragment: Fragment(), MavericksView {
    
    

  // 获取或创建一个和父Fragment共享的OrderViewModel实例
  private val viewModel: OrderViewModel by parentFragmentViewModel()

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    
    
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_order_detail, container, false)
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
    
    super.onViewCreated(view, savedInstanceState)

    // 绑定视图和数据
    binding.viewModel = viewModel
    binding.lifecycleOwner = viewLifecycleOwner

    // 设置点击事件
    binding.buttonEdit.setOnClickListener {
    
    
      viewModel.editOrder()
    }
  }

  override fun invalidate() {
    
    
    // 更新UI
    withState(viewModel) {
    
     state ->
      binding.textViewOrderDetail.text = state.orderDetail
      binding.textViewOrderNote.text = state.orderNote
    }
  }
}

- targetFragmentViewModel: To use this function in Fragment, you can write it like this:
class OrderSummaryFragment: Fragment(), MavericksView {
    
    

  // 获取或创建一个和目标Fragment(通过setTargetFragment设置)共享的OrderViewModel实例
  private val viewModel: OrderViewModel by targetFragmentViewModel()

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    
    
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_order_summary, container, false)
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
    
    super.onViewCreated(view, savedInstanceState)

    // 绑定视图和数据
    binding.viewModel = viewModel
    binding.lifecycleOwner = viewLifecycleOwner

    // 设置点击事件
    binding.buttonPay.setOnClickListener {
    
    
      viewModel.payOrder()
      findNavController().navigate(R.id.action_orderSummaryFragment_to_paymentFragment)
    }
  }

  override fun invalidate() {
    
    
    // 更新UI
    withState(viewModel) {
    
     state ->
      binding.textViewOrderSummary.text = state.orderSummary
      binding.textViewTotalPrice.text = state.totalPrice.toString()
      binding.textViewDiscount.text = state.discount.toString()
      binding.textViewTax.text = state.tax.toString()
      binding.textViewFinalPrice.text = state.finalPrice.toString()
    }
  }
}

- existingViewModel: To use this function in Fragment, you can write it like this:
class PaymentFragment: Fragment(), MavericksView {
    
    

  // 获取一个已经存在的OrderViewModel实例,如果没有找到,会抛出异常
  private val viewModel: OrderViewModel by existingViewModel()

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    
    
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_payment, container, false)
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
    
    super.onViewCreated(view, savedInstanceState)

     // 绑定视图和数据
     binding.viewModel = viewModel
     binding.lifecycleOwner = viewLifecycleOwner

     // 设置点击事件
     binding.buttonSubmit.setOnClickListener {
    
    
       viewModel.submitPayment()
       findNavController().navigate(R.id.action_paymentFragment_to_thankYouFragment)
     }
   }

   override fun invalidate() {
    
    
     // 更新UI
     withState(viewModel) {
    
     state ->
       binding.textViewFinalPrice.text = state.finalPrice.toString()
       binding.editTextCardNumber.setText(state.cardNumber)
       binding.editTextCardName.setText(state.cardName)
       binding.editTextCardExpiry.setText(state.cardExpiry)
       binding.editTextCardCvv.setText(state.cardCvv)
     }
   }
}

- viewModel: To use this function in Activity, you can write it like this:
class OrderActivity : AppCompatActivity() {
    
    

   // 获取或创建一个新的OrderViewModel实例
   private val viewModel : OrderViewModel by viewModel()

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

       setContentView(R.layout.activity_order)

       // 设置标题栏文字为订单号码
       supportActionBar?.title = "Order #${viewModel.state.orderNumber}"
   }
}

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by a senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically and efficiently. Master the various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Welcome everyone to support with one click and three links. If you need the information in the article, you can directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

PS: There is also a ChatGPT robot in the group, which can answer your work or technical questions

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/131463263