问题描述
我有一个带有2个recyclerview的viewpager (试图在Instagram用户个人资料中实施帖子部分) 问题是我不断出现内存泄漏。当我阅读LeakCanary日志时,它说内存泄漏的原因是viewpager。这是leakCanary片段
2020-05-13 17:57:32.914 D/LeakCanary: ┬───
2020-05-13 17:57:32.914 D/LeakCanary: │ GC Root: Local variable in native code
2020-05-13 17:57:32.914 D/LeakCanary: │
2020-05-13 17:57:32.914 D/LeakCanary: ├─ android.net.ConnectivityThread instance
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (PathClassLoader↓ is not leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ Thread name: 'ConnectivityThread'
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ ConnectivityThread.contextClassLoader
2020-05-13 17:57:32.914 D/LeakCanary: ├─ dalvik.system.PathClassLoader instance
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking and A ClassLoader is never leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ PathClassLoader.runtimeInternalObjects
2020-05-13 17:57:32.914 D/LeakCanary: ├─ java.lang.Object[] array
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ Object[].[349]
2020-05-13 17:57:32.914 D/LeakCanary: ├─ androidx.databinding.ViewDataBinding class
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (RouteInfoFragment↓ is not leaking and a class is never leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ static ViewDataBinding.sReferenceQueue
2020-05-13 17:57:32.914 D/LeakCanary: ├─ java.lang.ref.ReferenceQueue instance
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (RouteInfoFragment↓ is not leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ ReferenceQueue.head
2020-05-13 17:57:32.914 D/LeakCanary: ├─ androidx.databinding.ViewDataBinding$WeakListener instance
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (RouteInfoFragment↓ is not leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ ViewDataBinding$WeakListener.mObservable
2020-05-13 17:57:32.914 D/LeakCanary: ├─ androidx.databinding.ViewDataBinding$LiveDataListener instance
2020-05-13 17:57:32.914 D/LeakCanary: │ Leaking: NO (RouteInfoFragment↓ is not leaking)
2020-05-13 17:57:32.914 D/LeakCanary: │ ↓ ViewDataBinding$LiveDataListener.mLifecycleOwner
2020-05-13 17:57:32.914 D/LeakCanary: ├─ iclaude.berlinwanderer.features.route.ui.route_dashboard.route_info.RouteInfoFragment instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: NO (RouteDashboardFragment↓ is not leaking and Fragment#mFragmentManager is not null)
2020-05-13 17:57:32.915 D/LeakCanary: │ Fragment.mTag=f0
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ RouteInfoFragment.mParentFragment
2020-05-13 17:57:32.915 D/LeakCanary: ├─ iclaude.berlinwanderer.features.route.ui.route_dashboard.RouteDashboardFragment instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: NO (Fragment#mFragmentManager is not null)
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ RouteDashboardFragment.mLifecycleRegistry
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~~~~~~~~~~~~~~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ androidx.lifecycle.LifecycleRegistry instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ LifecycleRegistry.mObserverMap
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~~~~~~~~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ androidx.arch.core.internal.FastSafeIterableMap instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ FastSafeIterableMap.mHashMap
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~~~~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ java.util.HashMap instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ HashMap.table
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ java.util.HashMap$Node[] array
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ HashMap$Node[].[1]
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ java.util.HashMap$Node instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ HashMap$Node.key
2020-05-13 17:57:32.915 D/LeakCanary: │ ~~~
2020-05-13 17:57:32.915 D/LeakCanary: ├─ androidx.viewpager2.adapter.FragmentStateAdapter$5 instance
2020-05-13 17:57:32.915 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.915 D/LeakCanary: │ Anonymous class implementing androidx.lifecycle.LifecycleEventObserver
2020-05-13 17:57:32.915 D/LeakCanary: │ ↓ FragmentStateAdapter$5.this$0
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ iclaude.berlinwanderer.features.route.ui.route_dashboard.RouteDashboardViewPagerAdapter instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ RouteDashboardViewPagerAdapter.mObservable
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ androidx.recyclerview.widget.RecyclerView$AdapterDataObservable instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ RecyclerView$AdapterDataObservable.mObservers
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ java.util.ArrayList instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ ArrayList.elementData
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ java.lang.Object[] array
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ Object[].[0]
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ com.google.android.material.tabs.TabLayoutMediator$PagerAdapterObserver instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ TabLayoutMediator$PagerAdapterObserver.this$0
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ com.google.android.material.tabs.TabLayoutMediator instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: UNKNowN
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ TabLayoutMediator.tabLayout
2020-05-13 17:57:32.916 D/LeakCanary: │ ~~~~~~~~~
2020-05-13 17:57:32.916 D/LeakCanary: ├─ com.google.android.material.tabs.TabLayout instance
2020-05-13 17:57:32.916 D/LeakCanary: │ Leaking: YES (View detached and has parent)
2020-05-13 17:57:32.916 D/LeakCanary: │ mContext instance of iclaude.berlinwanderer.features.route.ui.RouteActivity with mDestroyed = false
2020-05-13 17:57:32.916 D/LeakCanary: │ View#mParent is set
2020-05-13 17:57:32.916 D/LeakCanary: │ View#mAttachInfo is null (view detached)
2020-05-13 17:57:32.916 D/LeakCanary: │ View.mID = R.id.tab_layout
2020-05-13 17:57:32.916 D/LeakCanary: │ View.mWindowAttachCount = 1
2020-05-13 17:57:32.916 D/LeakCanary: │ ↓ TabLayout.mParent
2020-05-13 17:57:32.916 D/LeakCanary: ╰→ androidx.constraintlayout.motion.widget.MotionLayout instance
2020-05-13 17:57:32.916 D/LeakCanary: Leaking: YES (ObjectWatcher was watching this because iclaude.berlinwanderer.features.route.ui.route_dashboard.RouteDashboardFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
2020-05-13 17:57:32.916 D/LeakCanary: key = 9c28ffc6-b1ce-4316-b015-c4df278892a1
2020-05-13 17:57:32.916 D/LeakCanary: watchDurationMillis = 36154
2020-05-13 17:57:32.916 D/LeakCanary: retainedDurationMillis = 31130
2020-05-13 17:57:32.916 D/LeakCanary: mContext instance of iclaude.berlinwanderer.features.route.ui.RouteActivity with mDestroyed = false
2020-05-13 17:57:32.917 D/LeakCanary: View#mParent is null
2020-05-13 17:57:32.917 D/LeakCanary: View#mAttachInfo is null (view detached)
2020-05-13 17:57:32.917 D/LeakCanary: View.mID = R.id.ml_main
2020-05-13 17:57:32.917 D/LeakCanary: View.mWindowAttachCount = 1
这是我的代码
ProfileFragment
这是使用viewpager的片段import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.viewmodels
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
import com.bumptech.glide.request.RequestOptions
import com.fdev.instagramclone.R
import com.fdev.instagramclone.business.domain.model.User
import com.fdev.instagramclone.databinding.FragmentProfileBinding
import com.fdev.instagramclone.framework.datasource.network.implementation.UserFirestoreServiceImpl
import com.fdev.instagramclone.framework.presentation.main.BaseMainFragment
import com.fdev.instagramclone.util.printLogD
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
@FlowPreview
@ExperimentalCoroutinesApi
@AndroidEntryPoint
class ProfileFragment : BaseMainFragment(R.layout.fragment_profile),PhotoGridAdapter.Interaction,PostViewPagerAdapter.Interaction {
companion object {
const val PAGE_NUMBER = 2
}
private var _binding: FragmentProfileBinding? = null
private val binding
get() = _binding!!
private val viewmodel: Profileviewmodel by viewmodels()
private var _postViewPagerAdapter: PostViewPagerAdapter? = null
private val postViewPagerAdapter
get() = _postViewPagerAdapter!!
private val tabDrawable = listof(R.drawable.ic_baseline_grid_on_24,R.drawable.ic_baseline_account_Box_24)
private var requestManager: RequestManager? = null
lateinit var tabLayoutMediator: TabLayoutMediator
private var images1 = listof(
"https://www.worldwomanfoundation.com/wp-content/uploads/2018/09/Jeannette_Ceja-_Head_Shot_2018_0-770x330.jpg","https://womensagenda.com.au/wp-content/uploads/2020/05/Sarah-Hill-002-1024x683.jpeg","https://news.harvard.edu/wp-content/uploads/2020/06/Durba_Mitra-copy_2500-1200x800.jpg","https://images.idgesg.net/images/article/2020/01/women-in-it_daphne-jones-100828118-large.jpg","https://vtnews.vt.edu/content/vtnews_vt_edu/en/articles/2020/04/science-women-in-data-science-online/_jcr_content/article-image.transform/m-medium/image.jpg",)
private var images2 = listof(
"https://koreanindo.net/wp-content/uploads/2019/02/everglow-wang-yiren.jpg","https://kepoper.com/wp-content/uploads/2019/10/Mina-Twice-Wiki-1-758x620.jpg","https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQT1uidd0fswlshWotS43XDLyscw6ZtFw-E3Q&usqp=CAU","https://cdn.idntimes.com/content-images/community/2020/04/20200417-142524-373be8fe5157e9ca9a24a8951ab21dda.jpg","https://i.redd.it/21csbponb0551.jpg","https://i.pinimg.com/originals/24/5d/87/245d87d800391ff353c04aed8eb52a50.jpg","https://images.idgesg.net/images/article/2020/01/women-in-it_daphne-jones-100828118-large.jpg"
)
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {
_binding = FragmentProfileBinding.inflate(inflater,container,false)
val view = binding.root
return view
}
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
initObserver()
initUI(view)
postViewPagerAdapter.addItemToPost(images2)
postViewPagerAdapter.addItemToTagged(images1)
}
private fun initUI(view: View) {
initViewPager(view)
}
private fun initViewPager(view: View) {
setupGlide()
_postViewPagerAdapter = PostViewPagerAdapter(requestManager as RequestManager,this,this)
binding.contentViewpager.adapter = postViewPagerAdapter
tabLayoutMediator = TabLayoutMediator(binding.contentTabLayout,binding.contentViewpager) { tab,position ->
tab.setIcon(tabDrawable[position])
}
tabLayoutMediator.attach()
}
private fun initObserver() {
viewmodel.sessionManager.currentUser.observe(viewLifecycleOwner,{ user ->
user?.let {
setUI(it)
}
})
}
private fun setUI(user: User) {
binding.apply {
usernameTv.text = user.username
followersTv.text = user.followers.size.toString()
followingTv.text = user.following.size.toString()
bioTv.text = user.bio
fullnameTv.text = user.name
if (!user.profileImage.equals(UserFirestoreServiceImpl.USER_DEFAULT_PICTURE_URL)) {
(requestManager as RequestManager)
.load(user.profileImage)
.into(profileCircleimageview)
}
}
}
private fun setupGlide() {
val requestOptions = RequestOptions
.placeholderOf(R.drawable.default_image)
.error(R.drawable.ic_baseline_error_outline_24)
activity?.let {
requestManager = Glide.with(it)
.applyDefaultRequestOptions(requestOptions)
}
}
override fun onItemSelected(position: Int,item: String,type: AdapterType) {
when (type) {
AdapterType.Post -> {
Toast.makeText(context,"Post $position",Toast.LENGTH_SHORT).show()
}
AdapterType.Tagged -> {
Toast.makeText(context,"Tagged $position",Toast.LENGTH_SHORT).show()
}
}
}
override fun onNextPage(type: Int) {
}
override fun onPause() {
super.onPause()
binding.swipeRefreshLayout.isEnabled = false
}
override fun afterScrolled(isFirstItem: Boolean) {
binding.swipeRefreshLayout.isEnabled = isFirstItem
}
override fun onDestroyView() {
super.onDestroyView()
tabLayoutMediator.detach()
// binding.contentViewpager.adapter = null
requestManager = null
_binding = null
}
}
ViewPagerAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.*
import com.bumptech.glide.RequestManager
import com.fdev.instagramclone.R
import com.fdev.instagramclone.databinding.ViewpagerPostItembinding
import com.fdev.instagramclone.util.printLogD
import kotlinx.android.synthetic.main.viewpager_post_item.view.*
class PostViewPagerAdapter(
var requestManager: RequestManager,interaction: PhotoGridAdapter.Interaction,var onNextPageInteraction: Interaction
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object{
const val POST = 1
const val TAGGED = 2
}
private var adapterList = ArrayList<PhotoGridAdapter>()
private var postAdapter: PhotoGridAdapter
private var taggedAdapter: PhotoGridAdapter
init {
postAdapter = PhotoGridAdapter(
requestManager,interaction,AdapterType.Post
)
taggedAdapter = PhotoGridAdapter(
requestManager,AdapterType.Tagged
)
adapterList.add(postAdapter)
adapterList.add(taggedAdapter)
}
override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {
// val binding = ViewpagerPostItembinding.inflate(
// LayoutInflater.from(parent.context),// parent,false
// )
val view = LayoutInflater.from(parent.context).inflate(
R.layout.viewpager_post_item,parent,false
)
return ViewPagerAdapterViewHolder(view,onNextPageInteraction)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder,position: Int) {
when (holder) {
is ViewPagerAdapterViewHolder -> {
holder.bind(adapterList[position])
}
}
}
override fun getItemCount(): Int {
return adapterList.size
}
fun addItemToTagged(list : List<String>) {
taggedAdapter.preloadGlideImages(
requestManager,list
)
taggedAdapter.submitList(list)
}
fun addItemToPost(list : List<String>) {
postAdapter.preloadGlideImages(
requestManager,list
)
postAdapter.submitList(list)
}
class ViewPagerAdapterViewHolder
constructor(
var itemView : View,var interaction: Interaction,) : RecyclerView.ViewHolder(itemView) {
fun bind(item : PhotoGridAdapter) = with(itemView) {
initRecylerView(itemView.post_recylerview,item)
}
private fun initRecylerView(recyclerView: RecyclerView,recyclerViewAdapter : PhotoGridAdapter){
recyclerView.apply{
val gridLayout = GridLayoutManager( context,3)
layoutManager = gridLayout
setHasFixedSize(true)
addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrollStateChanged(recyclerView: RecyclerView,newState: Int) {
super.onScrollStateChanged(recyclerView,newState)
val layoutManager = recyclerView.layoutManager as linearlayoutmanager
val lastPosition = layoutManager.findLastVisibleItemPosition()
if(lastPosition == recyclerViewAdapter.itemCount.minus(1)){
interaction.onNextPage(adapterPosition)
}
}
})
addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView,dx: Int,dy: Int) {
super.onScrolled(recyclerView,dx,dy)
interaction.afterScrolled(gridLayout.findFirstCompletelyVisibleItemPosition() == 0)
}
})
adapter = recyclerViewAdapter
}
}
}
interface Interaction {
fun onNextPage(type : Int)
fun afterScrolled(isFirstItem : Boolean)
}
}
Recylerview适配器(viewpager的内容)
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import com.bumptech.glide.RequestManager
import com.fdev.instagramclone.databinding.PhotoGridItemContainerBinding
import com.bumptech.glide.load.resource.drawable.DrawableTransitionoptions.withCrossFade
import com.fdev.instagramclone.util.printLogD
class PhotoGridAdapter(
private val requestManager: RequestManager,private val interaction: Interaction? = null,private val type : AdapterType
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String,newItem: String): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: String,newItem: String): Boolean {
return oldItem == newItem
}
}
private val differ = AsyncListDiffer(this,DIFF_CALLBACK)
override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {
val itembinding = PhotoGridItemContainerBinding.inflate(
LayoutInflater.from(
parent.context
),false
)
return PhotoGridViewHolder(itembinding,requestManager,type)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder,position: Int) {
when (holder) {
is PhotoGridViewHolder-> {
holder.bind(differ.currentList.get(position))
}
}
}
override fun getItemCount(): Int {
return differ.currentList.size
}
fun submitList(list: List<String>) {
differ.submitList(list)
}
//To preload the image and cache it
fun preloadGlideImages(
requestManager: RequestManager,imagesURL : List<String>
){
for(url in imagesURL){
requestManager
.load(url)
.preload()
}
}
class PhotoGridViewHolder
constructor(
private val photoGridBinding: PhotoGridItemContainerBinding,private val interaction: Interaction?,private val requestManager: RequestManager,private val type : AdapterType
) : RecyclerView.ViewHolder(photoGridBinding.root) {
fun bind(item: String) = with(photoGridBinding) {
postimage.setonClickListener {
interaction?.onItemSelected(adapterPosition,item,type)
}
requestManager
.load(item)
.transition(withCrossFade())
.into(postimage)
}
}
interface Interaction {
fun onItemSelected(position: Int,type : AdapterType)
}
}
enum class AdapterType {
Tagged,Post
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)