Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/182.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 当使用Glide在RecyclerView中加载图像时,UI会滞后且不稳定_Android_Android Recyclerview_Google Cloud Firestore_Android Glide - Fatal编程技术网

Android 当使用Glide在RecyclerView中加载图像时,UI会滞后且不稳定

Android 当使用Glide在RecyclerView中加载图像时,UI会滞后且不稳定,android,android-recyclerview,google-cloud-firestore,android-glide,Android,Android Recyclerview,Google Cloud Firestore,Android Glide,我有一个RecyclerView,它使用Glide从URL加载图像。现在使用分页从Firebase检索URL,如下所示。问题在于,当包含以下代码和recyclerview的MainActivity首次初始化时,UI中存在严重的延迟,非常滞后且不稳定的滚动,选项菜单需要3秒钟才能打开,等等,图像需要一段时间才能加载。在我向下滚动并到达第一页数据的RecyclerView的末尾后,OnScrollListener被触发,我开始从新查询加载新数据。我已经尽了最大努力,根据我在另一篇文章中用户的建议来优

我有一个RecyclerView,它使用Glide从URL加载图像。现在使用分页从Firebase检索URL,如下所示。问题在于,当包含以下代码和recyclerview的MainActivity首次初始化时,UI中存在严重的延迟,非常滞后且不稳定的滚动,选项菜单需要3秒钟才能打开,等等,图像需要一段时间才能加载。在我向下滚动并到达第一页数据的RecyclerView的末尾后,OnScrollListener被触发,我开始从新查询加载新数据。我已经尽了最大努力,根据我在另一篇文章中用户的建议来优化Glide的功能,我还将adapter.setHasFixedSize设置为true,但没有运气。知道这里发生了什么吗?尽管查询是异步的,我还是挂起了UI线程吗

编辑 :Glide是否会因为必须将多个图像加载到recycler视图的ImageView中而导致主线程延迟?如果是这样,我能做些什么来应对

以下是我如何处理从Firebase获取的数据分页并通知适配器:

class MainActivity : AppCompatActivity() {

private val TAG: String = MainActivity::class.java.simpleName // Tag used for debugging
private var queryLimit : Long = 50 // how many documents should the query request from firebase
private lateinit var iconsRCV : RecyclerView // card icons recycler view
private lateinit var lastVisible:DocumentSnapshot

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rootRef: FirebaseFirestore = FirebaseFirestore.getInstance()
    val urlsRef : CollectionReference = rootRef.collection("CardIconUrls")
    val query : Query = urlsRef.orderBy("resID",Query.Direction.ASCENDING).limit(queryLimit) // create a query for the first queryLimit documents in the urlsRef collection

    // Setting Toolbar default settings
    val toolbar : Toolbar = findViewById(R.id.mainToolbar)
    setSupportActionBar(toolbar) // set the custom toolbar as the support action bar
    supportActionBar?.setDisplayShowTitleEnabled(false) // remove the default action bar title

    // RecyclerView initializations
    iconsRCV = findViewById(R.id.cardIconsRCV)
    iconsRCV.layoutManager = GridLayoutManager(this,5) // set the layout manager for the rcv
    val iconUrls : ArrayList<String> = ArrayList() // initialize the data with an empty array list
    val adapter = CardIconAdapter(this,iconUrls) // initialize the adapter for the recyclerview
    iconsRCV.adapter = adapter // set the adapter
    iconsRCV.setHasFixedSize(true)

    iconsRCV.addOnScrollListener(object:OnScrollListener(){
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)

            if(!iconsRCV.canScrollVertically(1) && (newState == RecyclerView.SCROLL_STATE_IDLE) && ((iconsRCV.layoutManager as GridLayoutManager).findLastVisibleItemPosition() == (iconsRCV.layoutManager as GridLayoutManager).itemCount-1)) {
                Log.d(TAG,"End of rcv-Starting query")
                val nextQuery = urlsRef.orderBy("resID",Query.Direction.ASCENDING).startAfter(lastVisible).limit(queryLimit).get().addOnCompleteListener { task ->
                    if(task.isSuccessful) {
                        Log.d(TAG,"Next query called")
                        for(document:DocumentSnapshot in task.result!!) {
                            iconUrls.add(document.get("url").toString())
                        }
                        lastVisible = task.result!!.documents[task.result!!.size()-1]
                        adapter.notifyDataSetChanged()
                    }
                }
            }
        }
    })

    query.get().addOnCompleteListener {task: Task<QuerySnapshot> ->
        if(task.isSuccessful) {
            Log.d(TAG,"Success")
            for(document:DocumentSnapshot in task.result!!) {
                Log.d(TAG,"Task size = " + task.result!!.size())
                iconUrls.add(document.get("url").toString()) // add the url to the list
            }
            lastVisible = task.result!!.documents[task.result!!.size()-1]
            adapter.notifyDataSetChanged() // notify the adapter about the new data
        }
    }
}
以下是recyclerview适配器:

public class CardIconAdapter extends RecyclerView.Adapter<CardIconAdapter.ViewHolder> {

    private List<String> urlsList;
    private Context context;

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView iconImg;
        ViewHolder(@NonNull View view) {
            super(view);
            iconImg = view.findViewById(R.id.cardIcon);
        }
    }

    public CardIconAdapter(Context cntxt, List<String> data) {
        context = cntxt;
        urlsList = data;
    }

    @NonNull
    @Override
    public CardIconAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view =  LayoutInflater.from(parent.getContext()).inflate(R.layout.card_icons_rcv_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CardIconAdapter.ViewHolder holder, int position) {
        RequestOptions requestOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL);
        GlideApp.with(context).load(urlsList.get(position)).thumbnail(0.25f).centerCrop().dontTransform().apply(requestOptions).into(holder.iconImg);
    }

    @Override
    public int getItemCount() {
        return urlsList.size();
    }
}

加载大量图像时修复滞后的一些方法

将固定大小设置为您的RecyclerView的真值,但如果分页涉及

mRecyclerView.setHasFixedSize(true);
像这样使用glide覆盖图像的像素

Glide.with(this)
     .load(YOUR_URL)
     .apply(new RequestOptions().override(dimenInPx, dimenInPx)
     .placeholder(R.drawable.placeHolder).error(R.drawable.error_image))
     .into(imageview);
将hardwareAccelerated=true属性添加到清单内的活动标记中,如

<activity
     ...
     android:hardwareAccelerated="true"
     ...
/>

我认为这可能会有所帮助。

加载大量图像时修复滞后的一些方法

将固定大小设置为您的RecyclerView的真值,但如果分页涉及

mRecyclerView.setHasFixedSize(true);
像这样使用glide覆盖图像的像素

Glide.with(this)
     .load(YOUR_URL)
     .apply(new RequestOptions().override(dimenInPx, dimenInPx)
     .placeholder(R.drawable.placeHolder).error(R.drawable.error_image))
     .into(imageview);
将hardwareAccelerated=true属性添加到清单内的活动标记中,如

<activity
     ...
     android:hardwareAccelerated="true"
     ...
/>

我认为这可能会有所帮助。

尝试将分页库与recyclerview结合使用

链接参考:


尝试将分页库与recyclerview结合使用

链接参考:


经过反复试验,我终于解决了这个问题。我的第一个错误是没有发布recyclerview项的xml布局文件,因为这是性能问题的根源。第二个错误是,我使用的是LinearLayout,并将其自身的layout_width和layout_height属性设置为75dp,而不是嵌套在LinearLayout内部的ImageView属性,并将wrap_内容用于ImageView的各个属性。因此,为了解决性能问题,我做了以下工作:

我将LinearLayout更改为ConstraintLayout,我看到它总体上优化了很多 我将ConstraintLayout的layout_width&layout_height属性设置为wrap_content,最后 我将实际ImageView的layout_width&layout_height属性设置为75dp,这是我希望图像的实际大小 以下是更改后recyclerview每个项目的最终布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:padding="5dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/cardIcon"
        android:layout_width="75dp"
        android:layout_height="75dp"
        android:contentDescription="cardIcon"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>

经过反复试验,我终于解决了这个问题。我的第一个错误是没有发布recyclerview项的xml布局文件,因为这是性能问题的根源。第二个错误是,我使用的是LinearLayout,并将其自身的layout_width和layout_height属性设置为75dp,而不是嵌套在LinearLayout内部的ImageView属性,并将wrap_内容用于ImageView的各个属性。因此,为了解决性能问题,我做了以下工作:

我将LinearLayout更改为ConstraintLayout,我看到它总体上优化了很多 我将ConstraintLayout的layout_width&layout_height属性设置为wrap_content,最后 我将实际ImageView的layout_width&layout_height属性设置为75dp,这是我希望图像的实际大小 以下是更改后recyclerview每个项目的最终布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:padding="5dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/cardIcon"
        android:layout_width="75dp"
        android:layout_height="75dp"
        android:contentDescription="cardIcon"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
基本上,如果您设置了图像的精确尺寸,Glide就不再需要不断计算图像需要多大,并显著提高了性能。这有助于将我的RecyclerView从无法使用变为完美,只需在我加载的ImageView上设置一个定义的高度和宽度


基本上,如果您设置了图像的精确尺寸,Glide就不再需要不断计算图像需要多大,并显著提高了性能。这有助于将我的RecyclerView从无法使用变为完美,只需在我加载的ImageView上设置一个定义的高度和宽度。

自我复制。@MartinZeitler为什么它是重复的?在您链接的帖子中,我在一个RecyclerView中通过Glide快速加载图像时遇到了问题,问题已经解决,但用户仍然没有添加答案,在这篇文章中,我的RecyclerView滞后、UI滞后、删除以前的数据等方面出现了性能问题。对我来说,这似乎很正常
同样的问题;尝试缓存这些缩略图,而不是动态缩小它们,这可能会浪费电池,因为它需要CPU。已经按预定的尺寸提供了它们,就完全不需要操纵它们,而Glide也不需要这样做。我的意思是,缩小一张图片是没有问题的,但缩小一整组图片是有问题的。@MartinZeitler不会滑动自动缓存它们吗?即使我没有在飞行中向下采样,我仍然会遇到长时间的加载问题,但是现在recycler视图也落后了,而且以前的数据加载很奇怪,如果它们加载的话。你的recycler视图的实现有点不同。请发布firebase db的节点结构如何存储数据。@MartinZeitler的自复制为什么是重复的?在你链接的帖子中,我在一个RecyclerView中通过Glide快速加载图像时遇到了问题,问题已经解决,但用户仍然没有添加答案,而在这篇文章中,我的RecyclerView滞后、UI滞后、删除以前的数据等出现了性能问题。对我来说,这似乎是同样的问题;尝试缓存这些缩略图,而不是动态缩小它们,这可能会浪费电池,因为它需要CPU。已经按预定的尺寸提供了它们,就完全不需要操纵它们,而Glide也不需要这样做。我的意思是,缩小一张图片是没有问题的,但缩小一整组图片是有问题的。@MartinZeitler不会滑动自动缓存它们吗?即使我没有在飞行中向下采样,我仍然会遇到长时间的加载问题,但是现在recycler视图也落后了,而且以前的数据加载很奇怪,如果它们加载的话。你的recycler视图的实现有点不同。请发布firebase db的节点结构,说明数据将如何存储。遗憾的是,我看不出有任何区别,我的意思是,他们正在不停地加载,大约2分钟后仍在加载。这就是ui的外观:。我还注意到,在logcat中,我没有收到任何实际错误,因此我发现很难找到问题的根源。覆盖图像的高度和宽度可以提高recyclerview的性能,如果加载时需要显示进度,请参考这一点,我看不到任何差异,我的意思是,他们在2分钟后不停地加载,没有任何进展,仍然在运行。这就是ui的外观:。我还注意到,在logcat中,我没有收到任何实际错误,因此我发现很难找到问题的根源。覆盖图像的高度和宽度可以提高recyclerview性能,如果您需要在加载时显示进度,请参考此内容。正如您在我的代码中看到的,我实际上正在分页数据。我加载数据页面,其中每个页面由50个图标组成,只有在滚动到recycler视图底部后才会请求新的图标。我只是没有使用分页库,因为我认为对于如此简单的数据字符串,不值得浏览所有文档等。您是否尝试过类似的方法,并看到了显著的性能改进?正如您在代码中看到的,我实际上正在分页数据。我加载数据页面,其中每个页面由50个图标组成,只有在滚动到recycler视图底部后才会请求新的图标。我只是没有使用分页库,因为我认为对于如此简单的数据字符串,不值得浏览所有文档等。您是否尝试过类似的功能,并看到了显著的性能改进?