懒加载总结小收集

所谓懒加载(lazy)就是延时加载,延迟加载。

什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载。

至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,

因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,

我们让数据在需要的时候才进行加载,这时我们就用到了懒加载。

比如部门ENTITY和员工ENTITY,部门与员工1对多,如果lazy设置为 false,那么只要加载了一个部门的po,就会根据一对多配置的关系把所有员工的po也加载出来。但是实际上有时候只是需要用到部门的信息,不需要用到 员工的信息,这时员工po的加载就等于浪费资源。如果lazy设置为true,那么只有当你访问部门po的员工信息时候才回去加载员工的po的信息。




一对一的懒加载分析

一对一的懒加载并不常用,因为懒加载的目的是为了减少与数据库的交互,从而提高执行效率,而在一对一关系中,主表中的每一条数据只对应从表的一条数据库,就算都查询也不会增加多少交互的成本,而且主表不能有contrained=true,所以主表是不能懒加载的。(从表可以有)

 

注:当fetch设置为join时,懒加载就会失效。因为fetch的作用是抓取方式,他有两个值分别问select和join,默认值为select。即在设为join时,他会直接将从表信息以join方式查询到而不是再次使用select查询,这样导致了懒加载的失效。

 

一对多和多对多的懒加载分析

  与一对一关联不同,一对多和一对多的关联下,主表的每一条属性都会对应从表的多条数据,这个时候懒加载就显得非常有效了。比如一个部门里面有多个员工,如果没有懒加载,每查询这个部门的时候都会查询出多个员工,这会大大增加与数据库交互的成本。所以Hbernate默认的是加入懒加载的。这就是查询集合属性的时候返回的是一个PersistentIndexed*类型对象的原因。该对象其实就是一个代理对象。当然,可以在映射文件中通过将lazy属性设为假来禁用。

 

多对一的懒加载分析

  虽然多对一与一对一关系方式相同,但是在Hibernate中多对一时,默认是进行懒加载的。另外有一点需要注意的是懒加载并不会区分集合属性里面是否有值,即使是没有值,他依然会使用懒加载,这也是懒加载不够完善的地方之一。

 

懒加载的一些细节扩充

  有的时候,我们在session关闭后仍需要使用懒加载的代理对象来查询数据库,这时我们就需要将代理对象初始化,不过问题是,当我们不能确定初始化后就一定使用该对象的时候怎么办,这样不是又白白浪费了资源吗?我们可以在使用代理对象的方法里面加入一个布尔值参数,这样当我们不需要初始化代理对象的时候只要将布尔参数设为假。但也是有缺点的,因为在调用的时候,尤其是在别人使用的时候,参数越多,方法学习成本就会增加,所以请酌情处理。

  懒加载也可以用于某些简单属性,但是因为实现起来比较复杂,而且效果并不明显,所以并不推荐。




---------------------------------------------------------- 
get()与load()的区别 
get()无懒加载特性,马上执行SQL查询. 
load()有懒加载特性,会返加一个代理对象,所以永远不为null,先不执行SQL,要取对象的值时才执行SQL语句,前题session不能关闭,<class>标签上lazy不为false. 

---------------------------------------------------------- 
实现懒加载的前提:1 PO不能是final的 
2 能实现懒加载的对象(PO)都是被CGLIB改写的代理对象,所以不能是final修饰的 
3 须要asm,cglib两个jar包 
4 相应的lazy属性为true 
5 相应的fetch属性为select 
---------------------------------------------------------- 
什么时候出遇到懒加载 
1 使用load() 
2 一对一<one-to-one> 
查主对象 默认使用join连接,不会发生懒加载 
查从对象 默认会发生懒加载,先执行一句select查出从对象,当通过从对象访问了主对象时,再执行一句select查出主对象. 

3 多对一<many-to-one> 
在使用hbm.xnl时,取多的一方时,默认会懒加载,不取一的一方 
在使用JPA时,取多的一方时,默认自动使用join on语句取出一的一方(用户与组,用户是多的一方,组是一的一方) 

4 一对多(<set><list>) 
默认会懒加载,这是必须的,是重常用的。 

---------------------------------------------------------- 

<think>我们之前讨论了ListAdapter的submitList使用,现在用户转向一个新问题:Kotlin懒加载Fragment传递数据的方法。 用户需求:在懒加载Fragment中如何传递数据。 注意:懒加载通常指Fragment在真正对用户可见时才加载数据,以减少资源消耗。传递数据则是在创建Fragment时或创建后给Fragment传递参数。 关键点: 1. Fragment懒加载的实现方式:通常通过setUserVisibleHint(已废弃)或结合ViewPager2的FragmentStateAdapter行为,现在更推荐使用FragmentTransaction.setMaxLifecycle或ViewPager2配合BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT来实现懒加载。 2. 传递数据的方法:在创建Fragment时通过Bundle传递,或者通过ViewModel共享。 结合懒加载场景,传递数据的方法需要区分: - 在Fragment创建时传递(静态传递):使用arguments - 在Fragment创建后传递(动态传递):使用ViewModel或者接口回调 由于懒加载意味着在Fragment可见时才加载数据,因此传递数据可能发生在创建时(静态数据),也可能在创建后(动态数据)。在懒加载触发时(比如onResume中判断第一次可见),我们需要获取到这些数据。 方案一:使用arguments(静态传递) - 在创建Fragment时通过Bundle设置参数,然后在懒加载的时候(比如onResume中)从arguments中获取数据。 - 注意:这种方式适合数据在创建时就已经确定的情况。 方案二:使用ViewModel(动态传递) - 共享ViewModel:如果数据需要从Activity或其他Fragment传递过来,并且可能变化,可以使用ViewModel,结合LiveData或StateFlow。 - 在Fragment进入resumed状态(即懒加载触发时)开始观察ViewModel中的数据。 方案三:接口回调(动态传递) - 定义一个接口,在Activity中实现,Fragment在onAttach中获取接口实例,然后在懒加载时通过接口获取数据。 然而,在懒加载Fragment中,我们通常会在第一次可见时(比如在onResume中判断是否第一次可见)才加载数据,因此传递数据的方式和普通Fragment并无不同,关键在于何时使用这些数据。 步骤: 1. 传递数据(静态或动态) 2. 在懒加载的回调点(例如:onResume中判断用户可见且是第一次可见)获取数据并加载。 具体实现: 一、静态传递数据(使用arguments) 创建Fragment时: ```kotlin val fragment = MyFragment().apply { arguments = bundleOf("key" to value) // 使用AndroidX的bundleOf } ``` 在Fragment中懒加载时获取数据: ```kotlin class MyFragment : Fragment() { private var isLoaded = false // 确保只加载一次 override fun onResume() { super.onResume() if (!isLoaded) { val data = arguments?.getString("key") // 使用data加载数据 loadData(data) isLoaded = true } } } ``` 二、动态传递数据(使用ViewModel) 假设我们有一个共享的ViewModel(在Activity作用域): ```kotlin class SharedViewModel : ViewModel() { private val _data = MutableLiveData<String>() val data: LiveData<String> = _data fun setData(newData: String) { _data.value = newData } } ``` 在Activity中: ```kotlin class MainActivity : AppCompatActivity() { private lateinit var viewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(SharedViewModel::class.java) // 设置数据 viewModel.setData("Hello") } } ``` 在Fragment中懒加载时观察数据(注意:在懒加载时才观察,避免在后台就触发): ```kotlin class MyFragment : Fragment() { private lateinit var viewModel: SharedViewModel private var isLoaded = false override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java) } override fun onResume() { super.onResume() if (!isLoaded) { // 开始观察,注意这里我们只观察一次,所以可以使用observeOnce或者自己移除观察者 val observer = Observer<String> { data -> // 使用数据加载 loadData(data) isLoaded = true // 移除观察者,避免重复触发 viewModel.data.removeObserver(observer) } viewModel.data.observe(viewLifecycleOwner, observer) } } } ``` 但是,上面的观察方式可能因为数据已经存在而立即触发,从而在onResume中执行加载。如果希望只在懒加载时才开始观察,并且只获取一次最新数据,也可以使用其他方式(如SingleLiveEvent或StateFlow的首次收集)。 三、使用接口回调(动态传递) 定义一个接口: ```kotlin interface DataProvider { fun getData(): String } ``` Activity实现接口: ```kotlin class MainActivity : AppCompatActivity(), DataProvider { override fun getData(): String { return "Hello" } } ``` 在Fragment中: ```kotlin class MyFragment : Fragment() { private var dataProvider: DataProvider? = null private var isLoaded = false override fun onAttach(context: Context) { super.onAttach(context) if (context is DataProvider) { dataProvider = context } else { throw RuntimeException("$context must implement DataProvider") } } override fun onResume() { super.onResume() if (!isLoaded) { val data = dataProvider?.getData() // 使用数据加载 loadData(data) isLoaded = true } } } ``` 注意事项: - 懒加载的触发点:上述例子都是在onResume中通过标志位控制。实际项目中,如果使用ViewPager2,我们通常设置BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,这样只有当前Fragment会执行onResume,从而实现懒加载。 - 避免内存泄漏:接口回调中,在onDetach中将dataProvider置为null(上面的例子中,由于持有的是Activity上下文,且Activity比Fragment生命周期长,所以一般不会泄漏,但为了安全可以在onDetach中置空)。 总结:懒加载Fragment传递数据的方法和普通Fragment一样,只是使用数据的时机是在懒加载回调中(如onResume中判断第一次可见时)。根据数据来源和特性选择静态传递(arguments)或动态传递(ViewModel、接口)。 相关问题: 1. 如何确保Fragment的懒加载只执行一次? 2. 在ViewPager2中如何实现Fragment的懒加载? 3. 使用ViewModel传递数据接口回调方式有何优劣? </think>### Kotlin 懒加载 Fragment 传递数据的完整指南 在 Android 开发中,Fragment 的懒加载(Lazy Initialization)通过延迟资源加载优化性能,配合高效的数据传递机制可显著提升用户体验。以下是三种核心实现方案: --- #### 方案 1:通过 `Bundle` 静态传递(推荐) ```kotlin class LazyFragment : Fragment() { // 懒加载关键:by lazy 确保首次访问时执行 private val userId: Int by lazy { arguments?.getInt("USER_ID") ?: throw IllegalStateException("Missing USER_ID") } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { // 视图初始化时不加载数据 return inflater.inflate(R.layout.fragment_lazy, container, false) } override fun onResume() { super.onResume() // 懒加载触发点:当Fragment对用户可见时加载数据 loadUserData(userId) } private fun loadUserData(id: Int) { // 实际数据加载逻辑 viewModel.loadUser(id) } companion object { fun newInstance(userId: Int): LazyFragment { return LazyFragment().apply { arguments = bundleOf("USER_ID" to userId) // AndroidX 扩展函数 } } } } ``` **使用方式**: ```kotlin // Activity/FragmentManager 中调用 supportFragmentManager.beginTransaction() .add(R.id.container, LazyFragment.newInstance(123)) .commit() ``` **优势**: - ⚡ 数据在 `onResume()` 触发时加载,避免过早占用资源[^1] - 🔒 类型安全:编译时检查数据类型 - 📦 进程重建时自动恢复数据(通过 `savedInstanceState`) --- #### 方案 2:通过 ViewModel 动态传递(响应式) ```kotlin class SharedViewModel : ViewModel() { private val _userData = MutableLiveData<User>() val userData: LiveData<User> = _userData fun setUserData(user: User) { _userData.value = user } } class LazyFragment : Fragment() { private lateinit var viewModel: SharedViewModel override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 获取共享ViewModel viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java) // 懒加载观察者 viewModel.userData.observe(viewLifecycleOwner) { user -> updateUI(user) // 数据到达时更新UI } } } ``` **使用方式**: ```kotlin // 在Activity中传递数据 val user = User(id=123, name="Kai") viewModel.setUserData(user) ``` **优势**: - 🔄 支持数据实时更新(如网络请求返回) - 🧩 跨Fragment/Activity共享数据 - ♻️ 自动处理生命周期感知[^2] --- #### 方案 3:接口回调传递(灵活通信) ```kotlin interface DataProvider { fun getUserId(): Int } class LazyFragment : Fragment() { private var dataProvider: DataProvider? = null override fun onAttach(context: Context) { super.onAttach(context) // 验证宿主Activity实现接口 dataProvider = context as? DataProvider ?: throw ClassCastException("Host must implement DataProvider") } override fun onResume() { super.onResume() dataProvider?.getUserId()?.let { loadUserData(it) } } } // Activity实现接口 class HostActivity : AppCompatActivity(), DataProvider { override fun getUserId() = 123 // 返回动态数据 } ``` **适用场景**: - 需要动态计算的数据 - 跨层级组件通信 - 复杂业务逻辑解耦 --- ### 关键注意事项 1. **生命周期对齐**: - 使用 `viewLifecycleOwner` 避免内存泄漏(View 销毁时自动移除观察者)[^2] - 在 `onDestroyView()` 中清除临时引用 2. **性能优化**: ```kotlin // 使用异步加载避免卡顿 private fun loadUserData(id: Int) { viewLifecycleOwner.lifecycleScope.launch { val user = withContext(Dispatchers.IO) { repository.fetchUser(id) } updateUI(user) } } ``` 3. **懒加载最佳实践**: - 首次加载后禁用重复加载: ```kotlin private var isDataLoaded = false override fun onResume() { super.onResume() if (!isDataLoaded) { loadData() isDataLoaded = true } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值