Android:如何使用unregisterAdapterDataObserver取消适配器待处理更新
Hey there! Let’s break down how to solve this problem properly—first, let’s clear up a common misunderstanding about that hasPendingAdapterUpdates() method you found:
hasPendingAdapterUpdates()only checks if the RecyclerView has pending UI update tasks (like pendingnotifyDataSetChanged()calls or item animations). It cannot cancel your network requests, since those are separate from the RecyclerView’s UI update pipeline.
To stop in-flight image requests from updating your adapter when a new list comes in, you need to manage the lifecycle of those network requests directly. Here are two common approaches depending on how you’re loading images:
1. Using an Image Loading Library (Glide/Picasso)
Most popular image loading libraries have built-in tools to cancel requests, which is the easiest and most reliable way to handle this.
Example with Glide
Glide automatically ties requests to the lifecycle of your Views, but you can explicitly cancel requests when updating your list:
class YourAdapter : RecyclerView.Adapter<YourAdapter.ViewHolder>() { private var currentItems: List<YourModel> = emptyList() private val glide = Glide.with(YourApplication.getAppContext()) private var yourRecyclerView: RecyclerView? = null override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) yourRecyclerView = recyclerView } fun updateList(newItems: List<YourModel>) { // Cancel all active image requests for visible views cancelActiveImageRequests() currentItems = newItems notifyDataSetChanged() } private fun cancelActiveImageRequests() { yourRecyclerView?.let { rv -> for (i in 0 until rv.childCount) { val viewHolder = rv.getChildViewHolder(rv.getChildAt(i)) as ViewHolder glide.clear(viewHolder.imageView) } } } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = currentItems[position] glide.load(item.imageUrl) .into(holder.imageView) } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val imageView: ImageView = itemView.findViewById(R.id.your_image_view) } }
Example with Picasso
Picasso works similarly—you can call cancelRequest() on the ImageView tied to the request:
class YourAdapter : RecyclerView.Adapter<YourAdapter.ViewHolder>() { private var currentItems: List<YourModel> = emptyList() private var yourRecyclerView: RecyclerView? = null override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) yourRecyclerView = recyclerView } fun updateList(newItems: List<YourModel>) { yourRecyclerView?.let { rv -> for (i in 0 until rv.childCount) { val viewHolder = rv.getChildViewHolder(rv.getChildAt(i)) as ViewHolder Picasso.get().cancelRequest(viewHolder.imageView) } } currentItems = newItems notifyDataSetChanged() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = currentItems[position] Picasso.get().load(item.imageUrl).into(holder.imageView) } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val imageView: ImageView = itemView.findViewById(R.id.your_image_view) } }
2. Manual Network Requests (OkHttp/Retrofit)
If you’re handling image requests manually with OkHttp or Retrofit, you’ll need to track all active requests and cancel them when updating the list:
class YourAdapter : RecyclerView.Adapter<YourAdapter.ViewHolder>() { private var currentItems: List<YourModel> = emptyList() private val pendingRequests = mutableListOf<Call<Bitmap>>() private val imageApi = Retrofit.Builder() .baseUrl("your_base_url/") .addConverterFactory(BitmapConverterFactory.create()) .build() .create(ImageApi::class.java) fun updateList(newItems: List<YourModel>) { // Cancel all pending requests and clear the list cancelAllPendingRequests() currentItems = newItems notifyDataSetChanged() } private fun cancelAllPendingRequests() { pendingRequests.forEach { it.cancel() } pendingRequests.clear() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = currentItems[position] val imageCall = imageApi.fetchImage(item.imageUrl) pendingRequests.add(imageCall) imageCall.enqueue(object : Callback<Bitmap> { override fun onResponse(call: Call<Bitmap>, response: Response<Bitmap>) { // Only update the view if the request wasn't canceled and the response is valid if (!call.isCanceled && response.isSuccessful) { response.body()?.let { holder.imageView.setImageBitmap(it) } } // Remove the completed request from our tracking list pendingRequests.remove(call) } override fun onFailure(call: Call<Bitmap>, t: Throwable) { pendingRequests.remove(call) } }) } // Don't forget to cancel requests when the adapter is detached from the RecyclerView override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) cancelAllPendingRequests() } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val imageView: ImageView = itemView.findViewById(R.id.your_image_view) } // Define your Retrofit API interface interface ImageApi { @GET fun fetchImage(@Url imageUrl: String): Call<Bitmap> } }
Key Notes
- Always cancel requests when the adapter is detached from the RecyclerView (using
onDetachedFromRecyclerView()) to avoid memory leaks. - For manual requests, always check
call.isCanceledbefore updating the View—this prevents canceled requests from trying to modify a View that might be recycled for the new list.
内容的提问来源于stack exchange,提问作者Vanshika Arora

