Android Jetpack系列-实现Application作用域的共享ViewModel用于Activity和Fragment的相互通信

2023-05-16

目录

前言

搞清楚activityViewModels的本质

定义Application作用域的ViewMode

ApplicationViewModelLazy

BaseViewModelApplication

使用方法

自定义Application继承BaseViewModelApplication

applicationViewModels获取ViewModel实例

小结


前言

官方文档中提到Activity 中的两个或更多 Fragment 相互通信是使用ShareViewModel,通过扩展函数activityViewModels获取同一个Activity的ViewModelStore,从而得到保存在Activity中的同一个ViewModel的实例。

具体使用如下:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}

ListFragment和DetailFragment通过activityViewModels获取到的是同一个实例,因此可以互相通信。以上是两个或更多 Fragment 相互通信的方案,那如果是Activity和Fragment相互通信呢?按照这个思路我们是否可以实现一个Application级别的ViewModel?答案是肯定的!

搞清楚activityViewModels的本质

在依葫芦画瓢之前,必须得搞清楚activityViewModels是怎么实现的,跟踪进源码,发现activityViewModels是一个扩展函数,定义在FragmentViewModelLazy.kt文件中:

/**
 * Returns a property delegate to access [ViewModel] by **default** scoped to this [Fragment]:
 * ```
 * class MyFragment : Fragment() {
 *     val viewmodel: MYViewModel by viewmodels()
 * }
 * ```
 *
 * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
 * factory returned by it will be used to create [ViewModel]:
 * ```
 * class MyFragment : Fragment() {
 *     val viewmodel: MYViewModel by viewmodels { myFactory }
 * }
 * ```
 *
 * Default scope may be overridden with parameter [ownerProducer]:
 * ```
 * class MyFragment : Fragment() {
 *     val viewmodel: MYViewModel by viewmodels ({requireParentFragment()})
 * }
 * ```
 *
 * This property can be accessed only after this Fragment is attached i.e., after
 * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)

/**
 * Returns a property delegate to access parent activity's [ViewModel],
 * if [factoryProducer] is specified then [ViewModelProvider.Factory]
 * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
 * [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
 * will be used.
 *
 * ```
 * class MyFragment : Fragment() {
 *     val viewmodel: MyViewModel by activityViewModels()
 * }
 * ```
 *
 * This property can be accessed only after this Fragment is attached i.e., after
 * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

/**
 * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
 * to default factory.
 */
@MainThread
fun <VM : ViewModel> Fragment.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}

虽然代码量很少,但是先不管细枝末节,我们把核心骨架抽出来屡清楚思路。

可以看到activityViewModels返回的是createViewModelLazy方法创建的ViewModel实例,createViewModelLazy调用的是ViewModelLazy的实例,ViewModelLazy又是通过ViewModelProvider实例的get方法获取到ViewModel的实例。

图1

图1已经比较完整的把ViewModel实例的创建过程展示出来了。但是有个疑问,为什么不同的Fragment的获取到的是同一个ViewModel的实例?回答这个问题,我们按照图1进行逆向分析。

首先,ViewModel实例是ViewModelProvider.Factory工厂类创建的:

public open class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
) {
    /**
     * Implementations of `Factory` interface are responsible to instantiate ViewModels.
     */
    public interface Factory {
        /**
         * Creates a new instance of the given `Class`.
         *
         * @param modelClass a `Class` whose instance is requested
         * @return a newly created ViewModel
         */
        public fun <T : ViewModel> create(modelClass: Class<T>): T
    }
}


/**
 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
 * an activity), associated with this `ViewModelProvider`.
 *
 * The created ViewModel is associated with the given scope and will be retained
 * as long as the scope is alive (e.g. if it is an activity, until it is
 * finished or process is killed).
 *
 * @param key        The key to use to identify the ViewModel.
 * @param modelClass The class of the ViewModel to create an instance of it if it is not
 * present.
 * @return A ViewModel that is an instance of the given type `T`.
 */
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    var viewModel = store[key]
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    viewModel = if (factory is KeyedFactory) {
        factory.create(key, modelClass)
    } else {
        factory.create(modelClass)
    }
    store.put(key, viewModel)
    return viewModel
}

可以看到,get方法会先判断ViewModel实例是否已经存在,如果存在则直接返回,否则会通过工厂类Factory创建ViewModel实例并存到ViewModelStore的实例store中。这样一来,只要保证ViewModelStore的实例是同一个,那ViewModelProvider的get方法每次返回的都是同一个ViewModel实例。

我们再看ViewModelProvider的store是从哪里获取的,ViewModelProvider的get方法是在ViewModelLazy调用的,看ViewModelLazy的源码:

/**
 * An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and
 * [androidx.activity.ComponentActivity.viewmodels].
 *
 * [storeProducer] is a lambda that will be called during initialization, [VM] will be created
 * in the scope of returned [ViewModelStore].
 *
 * [factoryProducer] is a lambda that will be called during initialization,
 * returned [ViewModelProvider.Factory] will be used for creation of [VM]
 */
public class ViewModelLazy<VM : ViewModel> (
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}

可见store是ViewModelLazy的构造函数中的参数storeProducer(高阶函数可当做参数传递)返回的。

而ViewModelLazy又是在FragmentViewModelLazy.kt的createViewModelLazy使用的:

@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

/**
 * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
 * to default factory.
 */
@MainThread
fun <VM : ViewModel> Fragment.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}

可以看到,storeProducer是扩展函数activityViewModels中通过requireActivity().viewModelStore 获取的。只要这些Fragment是同一个宿主Activity,那获取到的viewModelStore就是同一个,最终通过activityViewModels获取到的ViewModel实例也是同一个。

定义Application作用域的ViewMode

根据以上分析,我们就可以依葫芦画瓢实现Application作用域的共享ViewModel用于Activity和Fragment的相互通信了。

ApplicationViewModelLazy

首先创建一个ApplicationViewModelLazy.kt文件,内容如下:

/**
 * Returns a property delegate to access application's [ViewModel],
 * if [factoryProducer] is specified then [ViewModelProvider.Factory]
 * returned by it will be used to create [ViewModel] first time. Otherwise, the BaseViewModelApplication's
 * [com.nxg.mvvm.BaseViewModelApplication.getDefaultViewModelProviderFactory](default factory)
 * will be used.
 *
 * ```
 * class MyAppCompatActivity : AppCompatActivity() {
 *     val viewmodel: MyViewModel by applicationViewModels()
 * }
 * ```
 *
 * This property can be accessed only after this AppCompatActivity is create i.e., after
 * [AppCompatActivity.onCreate()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> AppCompatActivity.applicationViewModels(
    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
) = createViewModelLazy(VM::class, { (applicationContext as BaseViewModelApplication).viewModelStore },
    factoryProducer ?: { (applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory })
/**
 * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
 * to default factory.
 */
@MainThread
fun <VM : ViewModel> AppCompatActivity.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        (applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}

/**
 * Returns a property delegate to access application's [ViewModel],
 * if [factoryProducer] is specified then [ViewModelProvider.Factory]
 * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
 * [com.nxg.mvvm.BaseViewModelApplication.getDefaultViewModelProviderFactory](default factory)
 * will be used.
 *
 * ```
 * class MyFragment : Fragment() {
 *     val viewmodel: MyViewModel by applicationViewModels()
 * }
 * ```
 *
 * This property can be accessed only after this Fragment is attached i.e., after
 * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
 */
@MainThread
inline fun <reified VM : ViewModel> Fragment.applicationViewModels(
    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
) = createViewModelLazy(VM::class, { (requireActivity().applicationContext as BaseViewModelApplication).viewModelStore },
    factoryProducer ?: { (requireActivity().applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory })

/**
 * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
 * to default factory.
 */
@MainThread
fun <VM : ViewModel> Fragment.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        (requireActivity().applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}

定义了两个扩展方法:AppCompatActivity.applicationViewModels和Fragment.applicationViewModels,用法和activityViewModels是一样的,但提供的viewModelStore是不一样的,可以看到applicationViewModels的viewModelStore是BaseViewModelApplication提供的。

BaseViewModelApplication

BaseViewModelApplication代码如下:

open class BaseViewModelApplication : Application(), ViewModelStoreOwner,HasDefaultViewModelProviderFactory{

    companion object {
        const val TAG = "BaseViewModelApplication"
    }

    // Lazily recreated from NonConfigurationInstances by getViewModelStore()
    private var mViewModelStore: ViewModelStore? = null
    private var mDefaultFactory: ViewModelProvider.Factory? = null


    /**
     * Returns the [ViewModelStore] associated with this application
     *
     *
     * Overriding this method is no longer supported and this method will be made
     * `final` in a future version of ComponentActivity.
     *
     * @return a `ViewModelStore`
     * @throws IllegalStateException if called before the Activity is attached to the Application
     * instance i.e., before onCreate()
     */
    @NonNull
    @Override
    override fun getViewModelStore(): ViewModelStore {
        ensureViewModelStore()
        return mViewModelStore as ViewModelStore
    }

    /**
     * Application不需要处理配置改变导致的重建
     */
    private fun  /* synthetic access */ensureViewModelStore() {
        if (mViewModelStore == null) {
            mViewModelStore = ViewModelStore()
        }
    }

    override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
        if(mDefaultFactory == null){
            mDefaultFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(this)
        }
       return mDefaultFactory as ViewModelProvider.Factory
    }
}

BaseViewModelApplication实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,用于提供ViewModelStore和ViewModelProvider.Factory 实例。这样一来,AppCompatActivity.applicationViewModels和Fragment.applicationViewModels获取到的都是保存在BaseViewModelApplication中ViewModelStore的同一个ViewModel实例。

使用方法

自定义Application继承BaseViewModelApplication

在app模块中自定义Application继承BaseViewModelApplication。

@HiltAndroidApp
class App : BaseViewModelApplication() {

    companion object {
        const val TAG = "AppApplication"
        private var INSTANCE: App by NotNullSingleValueVar()
        fun instance() = INSTANCE
    }

    //定义一个属性管理类,进行非空和重复赋值的判断
    private class NotNullSingleValueVar<T> : ReadWriteProperty<Any?, T> {
        private var value: T? = null
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value ?: throw IllegalStateException("application not initialized")
        }

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            this.value = if (this.value == null) value
            else throw IllegalStateException("application already initialized")
        }
    }


    override fun onCreate() {
        super.onCreate()
        INSTANCE = this
        Utils.init(this)
        LogUtil.enable = BuildConfig.DEBUG
        registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
    }


    override fun onLowMemory() {
        super.onLowMemory()
        Log.i(
            TAG,
            "onLowMemory: "
        )
    }

    override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
        Log.i(
            TAG,
            "onTrimMemory: "
        )
    }

    private val mActivityLifecycleCallbacks: ActivityLifecycleCallbacks =
        object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                Log.i(
                    TAG,
                    "onActivityCreated: " + activity::class.java.name
                )
            }

            override fun onActivityStarted(activity: Activity) {
                Log.i(
                    TAG,
                    "onActivityStarted: " + activity::class.java.name
                )
            }

            override fun onActivityResumed(activity: Activity) {
                Log.i(
                    TAG,
                    "onActivityResumed: " + activity::class.java.name
                )
            }

            override fun onActivityPaused(activity: Activity) {
                Log.i(
                    TAG,
                    "onActivityPaused: " + activity::class.java.name
                )
            }

            override fun onActivityStopped(activity: Activity) {
                Log.i(
                    TAG,
                    "onActivityStopped: " + activity::class.java.name
                )
            }

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
                Log.i(
                    TAG,
                    "onActivitySaveInstanceState: " + activity::class.java.name
                )
            }

            override fun onActivityDestroyed(activity: Activity) {
                Log.i(
                    TAG,
                    "onActivityDestroyed: " + activity::class.java.name
                )
            }

        }
}

注意,重写onCreate方法的时候别忘记调用super.onCreate()。

applicationViewModels获取ViewModel实例

假设有一个名为AppShareViewModel的ViewModel:

/**
 * 作用域范围为Application的共享ShareViewModel
 * 使用方法:
 * ```
 * class MyAppCompatActivity : AppCompatActivity() {
 *     val appShareViewModel: AppShareViewModel by applicationViewModels()
 * }
 *
 * class MyFragment : Fragment() {
 *     val appShareViewModel: AppShareViewModel by applicationViewModels()
 * }
 * ```
 */
class AppShareViewModel(application: Application) : AndroidViewModel(application) {

    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(UiState.HOME)

    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<UiState> = _uiState

    init {
        viewModelScope.launch {
            _uiState.value = UiState.HOME
        }
    }

    fun setUiState(state: UiState) {
        viewModelScope.launch {
            _uiState.value = state
        }
    }

    /**
     * 界面状态
     */
    enum class UiState {
        PERMISSION,HOME, START, PAUSE, PLAY
    }

}

在Activity和Fragment中获取AppShareViewModel的实例如下(支持Hilt):

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    companion object {
        const val TAG = "MainActivity"
    }

    private lateinit var binding: MainActivityBinding
	//作用域范围为Application的共享ShareViewModel
    private val appShareViewModel: AppShareViewModel by applicationViewModels()

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

        binding = MainActivityBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Start a coroutine in the lifecycle scope
        lifecycleScope.launch {
            // repeatOnLifecycle launches the block in a new coroutine every time the
            // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                appShareViewModel.uiState.collect {
                    // New value received
                    when (it) {
                        AppShareViewModel.UiState.PERMISSION -> {
                            
                        }
                        AppShareViewModel.UiState.HOME -> {
                           
                        }
                        AppShareViewModel.UiState.START -> {
                            
                        }
                        AppShareViewModel.UiState.PAUSE -> {
                            
                        }
                        AppShareViewModel.UiState.PLAY -> {
                          
                        }

                    }
                }
            }
        }
    }
}

@AndroidEntryPoint
class AudioRecordFragment : Fragment() {

    companion object {
        const val TAG = "AudioRecordFragment"
        fun newInstance() = AudioRecordFragment()
    }
	//作用域范围为Application的共享ShareViewModel
    private val appShareViewModel: AppShareViewModel by applicationViewModels()
    
    //作用域范围为Activity的共享ShareViewModel
    private val shareViewModel:ShareViewModel by activityViewModels()

    private val audioRecordViewModel: AudioRecordViewModel by viewModels()

	private var _binding: AudioRecordFragmentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = AudioRecordFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
 		......
        appShareViewModel.uiState.collect {
            // New value received
            when (it) {
                AppShareViewModel.UiState.PERMISSION -> {

                }
                AppShareViewModel.UiState.HOME -> {

                }
                AppShareViewModel.UiState.START -> {

                }
                AppShareViewModel.UiState.PAUSE -> {

                }
                AppShareViewModel.UiState.PLAY -> {

                }

            }
        }
    }

}


@AndroidEntryPoint
class AudioRecordListFragment : Fragment() {

    companion object {
        const val TAG = "AudioRecordListFragment"
        fun newInstance() = AudioRecordListFragment()
    }

   //作用域范围为Application的共享ShareViewModel
    private val appShareViewModel: AppShareViewModel by applicationViewModels()
    
    //作用域范围为Activity的共享ShareViewModel
    private val shareViewModel:ShareViewModel by activityViewModels()

    private val audioRecordListViewModel: AudioRecordListViewModel by viewModels()

	private var _binding: AudioRecordListFragmentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = AudioRecordListFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        appShareViewModel.uiState.collect {
            // New value received
            when (it) {
                AppShareViewModel.UiState.PERMISSION -> {

                }
                AppShareViewModel.UiState.HOME -> {

                }
                AppShareViewModel.UiState.START -> {

                }
                AppShareViewModel.UiState.PAUSE -> {

                }
                AppShareViewModel.UiState.PLAY -> {

                }

            }
        }
 		......
    }

}

然后可以愉快玩耍了。

小结

通过模仿activityViewModels源码,我们定义了两个扩展方法:AppCompatActivity.applicationViewModels和Fragment.applicationViewModels,实现了Application作用域的ViewModel,非常简单。但是这样做是不是对的?会不会带来其它问题比如内存泄露啥的?毕竟ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据,可在发生屏幕旋转等配置更改后继续留存。我们这么一搞,好像是违背了这个中心思想,后续笔者会另外写一篇文章分析这个问题,敬请期待。

写在最后,首先非常感谢您耐心阅读完整篇文章,坚持写原创且基于实战的文章不是件容易的事,如果本文刚好对您有点帮助,欢迎您给文章点赞评论,您的鼓励是笔者坚持不懈的动力。若文章有不对之处也欢迎指正,再次感谢。

学如逆水行舟,不进则退;心似平原走马,易放难追。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Jetpack系列-实现Application作用域的共享ViewModel用于Activity和Fragment的相互通信 的相关文章

  • Octree(八叉树)

    1 算法原理 八叉树 xff08 Octree xff09 是一种用于描述三维空间的树状数据结构 八叉树的每个节点表示一个正方体的体积元素 xff0c 每个节点有八个子节点 xff0c 将八个子节点所表示的体积元素加在一起就等于父节点的体积
  • DBSCAN聚类算法

    DBSCAN是一种非常著名的基于密度的聚类算法 其英文全称是 Density Based Spatial Clustering of Applications with Noise xff0c 意即 xff1a 一种基于密度 xff0c 对
  • 搭建网盘工具kk

    1 主要功能 支持用作简单文件列表 xff0c 无需数据库 xff0c 无用户管理 xff0c 分享等功能支持基本网盘功能 xff0c 需要安装MongoDB及Redis xff0c 支持用户注册 xff0c 文件分享 xff0c 用户目录
  • numpy中transpose详解

    transpose用于numpy中高维度数组的轴变换 xff0c 在二维情况下就是通常说的转置 该方法很不好理解 xff0c 本文详细介绍该方法 该方法有两个实现 xff0c 分别是numpy ndarray transpose和numpy
  • Python压缩文件

    1 标准库中的压缩模块 在我们常用的系统windows和Linux系统中有很多支持的压缩包格式 xff0c 包括但不限于以下种类 xff1a rar zip tar xff0c 以下的标准库的作用就是用于压缩解压缩其中一些格式的压缩包 2
  • LE-VINS:固态激光雷达增强的视觉惯性导航系统

    在光照剧烈变化 动态物体 弱纹理等视觉退化场景 xff0c 视觉路标点的深度估计难度急剧增加 xff0c 导致视觉惯性导航系统 VINS 的精度和鲁棒性恶化 采用非重复扫描原理的固态激光雷达 xff0c 为解决视觉路标点深度估计问题带来便利
  • Gradio介绍

    Gradio App 就是给 AI 算法工程师训练的模型赋予分享给大众的能力 从技术侧拆分 xff0c 由三个部分组成 xff1a 前端页面 43 后端接口 43 AI算法模型推理 Gradio 做了一件事情 xff0c 就是将这三个部分封
  • GitHub Copilot 快速入门

    GitHub Copilot 是 AI 结对程序员 可以使用 GitHub Copilot 在编辑器中获取整行或整个函数的建议 1 简介 让我们首先了解一些关于 GitHub Copilot 的内容 这是 GitHub 和 OpenAI 的
  • 电池连接接触电阻的优化研究

    金属与金属接触处的接触电阻主要受以下因素影响 xff1a 表面结构机械负载 表面结构可以通过表面平整度 表面氧化和吸水性进一步描述 电池组中的这些接头将采用焊接或螺栓连接方式 最初最容易想到的是螺栓母线接头 螺栓扭矩可用于估算力 xff0c
  • (二)CAS统一认证——自定义登录(数据库)

    简介 关于CAS的登录流程 xff0c overlay中只是一个简单的用户名密码登录 casuser Mellon xff0c 这个肯定是不能满足日常生产的需求的 xff0c 在日常开发中遇到最通用的情况就是从数据库中进行身份认证密码的比对
  • springcloud

    一 什么是SpringCloud xff1f Author xff1a 呆萌老师 QQ 2398779723 微信 it daimeng Spring Cloud是一个微服务框架 xff0c 相比Dubbo等RPC框架 Spring Clo
  • 计算机操作系统学习之多生产者多消费者问题

    文章目录 一 问题描述二 问题分析1 关系分析2 整理思路3 设置信号量4 具体实现5 补充 一 问题描述 有一个盘子 xff0c 每次只能放一个水果父亲专门往盘子里放苹果 xff0c 母亲专门往盘子里放橘子女儿专门等着吃盘子里的苹果 xf
  • 姿态角与欧拉角的关系

    1 姿态角与欧拉角 姿态角 xff1a 指的是机体坐标系与地理坐标系的夹角 欧拉角 xff1a 绕机体坐标系三个轴旋转的角度 关系 xff1a 绕某种旋转顺序的欧拉角与姿态角相等 xff08 1 xff09 在NED 北东地 坐标系 xff
  • 从入门到进阶,JAVA书籍的最佳阅读顺序!

    本文首发于知乎 xff0c 已获得1000 43 赞和收藏 原文链接 xff1a https www zhihu com question 269505829 answer 1791006152 先介绍下本人的情况 xff0c 希望对大家学
  • 设计数据密集型应用-C5-主从架构及同步延迟问题

    本文是 设计数据密集型应用 第5章学习笔记 什么是Replication Replication是在多台机器上维护的相同的数据 xff0c 即副本 保存副本的原因有以下几种 xff1a 减小延迟 xff1a 使得地理位置上数据离访问者更近
  • 第一条Pulsar消息发送

    什么是Pulsar pulsar是一个多租户 高性能server to srever消息解决方案 xff0c 最初由雅虎开发 xff0c 现在由apache维护 Pulsar的核心特性 xff1a 多集群云原生支持低延迟 良好的伸缩性多语言
  • 2014找工作总结-机会往往留给有准备的人

    转发请注明出处 xff1a 2014找工作总结 机会往往留给有准备的人 计算机专业同学的充电站 CSDN博客 其实我的求职过程在十一之前就已经结束了 xff0c 总体讲比较顺利 参加面试的几家公司基本都拿到了offer xff0c 分别是阿
  • 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现

    通过我这些天用C 43 43 读写bmp图像的经历 xff0c 摸索再摸索 xff0c 终于对bmp文件的结构 操作有了一定的了解 xff0c 下面就大概介绍bmp图片纯C 43 43 的读取 旋转和保存的实现过程 要用C 43 43 读取
  • 【数字图像处理】直方图均衡化详解及编程实现

    直方图均衡化的英文名称是Histogram Equalization 图像对比度增强的方法可以分成两类 一类是直接对比度增强方法 另一类是间接对比度增强方法 直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法 直方图拉伸是通过对比度拉
  • 【GPU编程】体绘制传输函数-分类(Volume Rendering Transfer function:Pre- VS Post-Classification)

    在科学可视化中 xff0c 我们所获得的体数据集经常是代表一些光学上的不同物理属性的单值 通常没有可行的方法可以从这样的数据中获得发射和吸收属性 因此用户必须采用某种映射方法给数据值分配光学属性值来决定数据中的不同结构的模样 这离的映射就被

随机推荐

  • 【OpenGL】理解GL_TRIANGLE_STRIP等绘制三角形序列的三种方式

    GL TRIANGLE STRIP绘制三角形方式很多时候令人疑惑 xff0c 在这里对其运作机理进行解释 一般情况下有三种绘制一系列三角形的方式 xff0c 分别是GL TRIANGLES GL TRIANGLE STRIP和GL TRIA
  • OpenJDK与JDK的区别分析

    OpenJDK与JDK的区别分析 一 以下是具体分析 xff1a 使用过LINUX的人都应该知道 xff0c 在大多数LINUX发行版本里 xff0c 内置或者通过软件源安装JDK的话 xff0c 都是安装的OpenJDK xff0c 那么
  • 【C++深入探索】Copy-and-swap idiom详解和实现安全自我赋值

    任何管理某资源的类比如智能指针需要遵循一个规则 xff08 The Rule of Three xff09 xff1a 如果你需要显式地声明一下三者中的一个 xff1a 析构函数 拷贝构造函数或者是拷贝赋值操作符 xff0c 那么你需要显式
  • 【Linux】Vim编辑器-批量注释与反注释

    vim编辑器 批量注释与反注释 在使用vim编写代码的时候 xff0c 经常需要用到批量注释与反注释一段代码 下面简要介绍其操作 方法一 块选择模式 插入注释 xff1a 用v进入virtual模式 用上下键选中需要注释的行数 按Contr
  • 【算法学习】图相关算法编程实现-深度优先遍历和广度优先遍历

    一 图的表示 图G 61 V E 要表示一个图 xff0c 通常有两种方法 xff1a 邻接表和邻接矩阵 两种方法都既可以表示有向图 xff0c 也可以表示无向图 邻接表表示由一个包含 V 个列表的数组组成 xff0c 其中每个列表对应V中
  • 【笔试面试题】腾讯2013实习生面试算法题及参考答案

    总结了一下自己遇到的以及同学遇到的面试算法题 xff0c 是技术二面 有几道题给出了参考答案 xff0c 还有几道没有好的思路 路过的大侠如果有好的思路请留个言交流下呗 1 八数码问题 xff1a 3 3的格子 xff0c 有1 8个数 x
  • 【Linux学习】epoll详解

    什么是 epoll epoll 是什么 xff1f 按照 man 手册的说法 xff1a 是为处理大批量句柄而作了改进的 poll 当然 xff0c 这不是 2 6 内核才有的 xff0c 它是在 2 5 44 内核中被引进的 epoll
  • 算法设计应该依赖抽象而不是业务

    很多时候 xff0c 算法的设计是归属于详细设计阶段的 一些公司甚至都没有设计而直接编码 这些往往导致很多算法的实现都混杂在业务模块中 典型的特点是 xff0c 这些算法会依赖于业务实体的某些属性的实现 举一个简单的例子 xff0c 我曾经
  • 做程序员老婆的幸福

    刚好看网上一个写程序员老公的 说说程序员老公的一些事情 请帮忙分析是不是典型程序员 xff0c 想起自己就是一个程序员 xff0c 也是一个老公 xff0c 却不以为然起来 虽然不以为然 xff0c 却并没有驳斥的任何意思 毕竟很多人都不一
  • 好习惯成就好程序员

    公司搞了一次技术峰会 xff0c 我有机会和大家聊了一下有关如何成功的问题 会上我向大家提出了我的想法 xff0c 好习惯才能成就好程序员 有很多人可能对这句话很不以为然 xff0c 我也不忙辩解 xff0c 先回答我下面的一个问题 现在我
  • 程序江湖:第三章 莫等闲白了少年头

    周五因为羽毛球比赛 xff0c 没有更新 xff0c 今天继续 读者反馈 xff1a 很多读者反馈主人公的名字比较土 呵呵这个没关系 xff0c 可以在整理的时候统一修改 另外有些人表示特别希望看到职场的故事 xff0c 这个放心 本就是这
  • 程序江湖:第二十章 讲标的前一晚上

    说明 xff1a 非常抱歉 xff0c 这周参加了太多的会议 原来写作也是需要心情的 xff0c 当没有心情的时候 xff0c 你都懒得动笔 欧阳明来到云南的最主要的目的 xff0c 是为了应对昆明客户要求的评标 就是客户邀请了几家资质还可
  • springboot项目搭建

    一 Springboot 基本概念 1 1 什么是 springboot Spring Boot是由Pivotal团队提供的全新框架 xff0c 其设计目的是用来简化新Spring应用的初始搭建以及开发过程 该框架使用了特定的方式来进行配置
  • 管理 VS. 面向对象设计

    我是在吃虾的时候 xff0c 突然想起这个关联的 管理 xff0c 往往就是给你一堆事 xff0c 然后再给你一些人 xff0c OK xff0c 你去做吧 这是你的使命 下面我们来做类比吧 首先一个问题 xff0c 你是认为人重要 xff
  • 技术管理案例:代码规范还要继续推行吗?

    这是实际工作中总结的一个典型案例 是真实的 写出来供大家参考 案例 xff1a 技术经理 Y 今年新到了一个产品部门 xff0c 发现原来产品的代码很乱 xff0c 遗留的问题很多 而现有的人员 xff0c 又大半是新招的 xff0c 很多
  • 苹果成功的根本:统筹创新

    这周的某天中午 xff0c 我们聊起了这个话题 xff0c 是关于苹果为什么成功的话题 很多人都说是因为苹果的创新能力很强 但是诺基亚也不缺乏创新啊 xff01 为什么苹果就能胜出呢 xff1f 我个人认为这有很多方面是因为乔布斯 xff0
  • 建立健康的职业发展观

    Google的Reader要下线了 xff0c 很多人都说这是因为Blog的没落 微博的兴起 xff0c 确实给了很多人表达情绪的方式 如果我能够用140个字 xff0c 清晰的表达我的观点 xff0c 并且能够让我的读者做到一点点认可 x
  • Android Gradle Plugins系列-01-自定义Gradle插件入门指南

    前言 本文内容已经有很多大佬写过了 xff0c 不过这里为了知识体系的完整 xff0c 就再写一遍 xff0c 并加入Maven Publish插件的使用 xff0c 不感兴趣跳过就好 官方文档 xff1a Developing Custo
  • Android Gradle Plugins系列-02-Maven Publish 插件踩坑指南

    前言 可能有读者会疑惑 xff0c Maven Publish 插件又是啥玩意 xff1f 确定不是Maven插件吗 xff1f 不要逗我 让笔者慢慢道来 xff0c 如果你刚好把AndroidStudio升级到Android Studio
  • Android Jetpack系列-实现Application作用域的共享ViewModel用于Activity和Fragment的相互通信

    目录 前言 搞清楚activityViewModels的本质 定义Application作用域的ViewMode ApplicationViewModelLazy BaseViewModelApplication 使用方法 自定义Appli