使用glide和recyclerview的CPU高使用率

时间:2018-12-08 00:44:29

标签: android optimization android-recyclerview android-glide

我正在努力地通过分组加载自定义项目布局。是特别的-当我使用滑行加载图像(图像约为12kb)时,在项目绑定上。遇到“冻结帧”。探查器显示,在滑行加载图像的那一刻,CPU使用率急剧上升。即使我一次添加3个项目,但它仍然滞后,但是我需要添加更多(3个和48个项目之间的cpu使用率没有太大差异,因此冻结大约相同的时间)。通过从资源/预缓存/直接下载加载该文件进行了测试,无论是否带有RequestOptions()-一切都相同

与该问题有关的一些代码:

HomeScreen.kt

var subscription = 0

//Transformation
var loading = false
var itemWidth = 120

class HomeScreen : AppCompatActivity() {

    val categoriesReference = FirebaseDatabase.getInstance().getReference("/categories")
    var photos = ArrayList<PhotoItem>()
    val adapter = GroupAdapter<ViewHolder>()
    var itemsAllowed = 48
    lateinit var manager: GridLayoutManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home_screen)
        itemWidth = getItemWidth(this@HomeScreen)
        setSupportActionBar(toolbar)
        window.exitTransition = null
        //Action bar options
        supportActionBar?.setDisplayShowHomeEnabled(true)
        supportActionBar?.title = "Все обои"

        //FIX BLINKING ON TRANSITIONS
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            /*var fade = Fade()
            fade.excludeTarget(toolbar, true)
            fade.excludeTarget(toolbar2, true)
            fade.excludeTarget(android.R.id.statusBarBackground, true);
            fade.excludeTarget(android.R.id.navigationBarBackground, true);
            getWindow().setEnterTransition(fade)
            getWindow().setExitTransition(fade)*/
        }

        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_FULLSCREEN)

        //Working with layout
        manager = GridLayoutManager(this, calculateNoOfColumns(this@HomeScreen))
        adapter.setHasStableIds(true)
        recycleView.isNestedScrollingEnabled = false
        recycleView.adapter = adapter
        recycleView.layoutManager = manager

        loadPageFully()
        addItemsToMenu()
        //Listener for navigation view
        navigView.setNavigationItemSelectedListener {
            /*if (it.toString() == "Купить тариф") {
                subscription = 1
                alert("Тариф успешно установлен") {
                    navigView.menu.clear()
                    addItemsToMenu()
                    loadPageFully()
                    yesButton { }
                    supportActionBar?.title = "Все обои"
                }.show()
                true
            } else {*/
            when(it.title.toString()){
                "Все обои" -> {
                    loadPageFully()
                }
            }
            drawerLayoutMain.closeDrawer(GravityCompat.START, true)
            for (i in 0 until navigView.menu.size()) {
                navigView.menu.getItem(i).setChecked(false)
            }
            it.setChecked(true)
            loadPageFully(it.toString())
            loadingImageView.visibility = View.GONE
            waveImageView.visibility = View.GONE
            supportActionBar?.title = it.toString()
            true
//            }
        }

        //Shuffle images in layout manager
        shuffleButton.onClick {
            photos.shuffle()
            adapter.clear()
            photos.forEach {
                if (manager.itemCount < itemsAllowed) {
                    adapter.add(it)
                }
            }
        }

        scrollView3.setOnScrollChangeListener(object : View.OnScrollChangeListener {
            override fun onScrollChange(v: View?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) {
                println(loading.toString())
                if (!loading && !scrollView3.canScrollVertically(1) && manager.itemCount > 0) {
                    loading = true
                    if (itemsAllowed == photos.size) {
                    } else if (itemsAllowed + 48 >= photos.size) {
                        for (i in 48.downTo(0)) {
                            if (itemsAllowed + i == photos.size) {
                                itemsAllowed += i
                                waveImageView.visibility = View.VISIBLE
                                doAsync {
                                    Thread.sleep(300)
                                    scrollView3.fullScroll(View.FOCUS_DOWN)
                                }
                            }
                        }
                    } else {
                        waveImageView.visibility = View.VISIBLE
                        doAsync {
                            Thread.sleep(300)
                            scrollView3.fullScroll(View.FOCUS_DOWN)
                        }
                        itemsAllowed += 48
                    }
                    checkOk()
                }
            }
        })

        recycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                when (newState) {
                    SCROLL_STATE_IDLE -> Glide.with(this@HomeScreen).resumeRequests()
                    SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING -> Glide.with(this@HomeScreen).pauseRequests()
                }
            }
        })
    }

    //On home button clicked
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            android.R.id.home -> {
                drawerLayoutMain.openDrawer(GravityCompat.START)
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    fun getEntries(categoryName: String = "All") {
//This fun with ChildEventListeners(Firebase SDK) gets urls and pushes them to photos no problem here because by the time lags occur they all are in photos.
        }

    var okToLoad = false
    var loaded = 0
    fun loadImages() {
        if (okToLoad) {
            okToLoad = false
            if (photos.size < itemsAllowed) {
                itemsAllowed = photos.size
            }
            loading = true
            try {
                for (i in manager.findLastVisibleItemPosition() + 1 until itemsAllowed) {
                    Glide.with(this@HomeScreen).load(photos[i].url).apply(
                        RequestOptions().diskCacheStrategy(
                            DiskCacheStrategy.ALL
                        )/*.skipMemoryCache(true)*/
                    ).apply(RequestOptions().dontTransform()/*.override(220, 330)*/)
                        .listener(object : RequestListener<Drawable> {
                            override fun onResourceReady(
                                resource: Drawable?,
                                model: Any?,
                                target: com.bumptech.glide.request.target.Target<Drawable>?,
                                dataSource: DataSource?,
                                isFirstResource: Boolean
                            ): Boolean {
                                loaded += 1
                                println("1:$loaded")
                                println("2:$itemsAllowed")
                                if (loaded == itemsAllowed) {
                                    Timer().schedule(object : TimerTask() {
                                        override fun run() {
                                            runOnUiThread {
                                                waveImageView.visibility = View.GONE
                                                shuffleButton.visibility = View.VISIBLE
                                                okToLoad = true
                                                loading = false
                                                loadingImageView.visibility = View.GONE
                                                for (i in manager.findLastVisibleItemPosition() + 1 until loaded) {
                                                    adapter.add(photos[i])
                                                }
                                            }
                                        }
                                    }, 1000)
                                }
                                return false
                            }

                            override fun onLoadFailed(
                                e: GlideException?,
                                model: Any?,
                                target: com.bumptech.glide.request.target.Target<Drawable>?,
                                isFirstResource: Boolean
                            ): Boolean {
                                return false
                            }
                        }).preload(220, 330)
                }
            } catch (e: IndexOutOfBoundsException) {
            } catch (e: FileNotFoundException) {
                Toast.makeText(this@HomeScreen, "Квота превышена", Toast.LENGTH_LONG).show()

            }
        }
    }

    fun checkOk() {
        doAsync {
            if (photos.size == 0) {
                okToLoad = false
                Thread.sleep(100)
                checkOk()
            } else {
                okToLoad = true
                uiThread {
                    loadImages()
                }
            }
        }
    }

    fun addItemsToMenu() {
        categoriesReference.addChildEventListener(object : ChildEventListener {
            override fun onChildAdded(p0: DataSnapshot, p1: String?) {
                navigView.menu.add(p0.key.toString()).setIcon(R.drawable.ic_bookmark_black_24dp)
            }

            override fun onCancelled(p0: DatabaseError) = Unit

            override fun onChildChanged(p0: DataSnapshot, p1: String?) = Unit

            override fun onChildMoved(p0: DataSnapshot, p1: String?) = Unit

            override fun onChildRemoved(p0: DataSnapshot) = Unit
        })
    }

    fun loadPageFully(key: String = "All") {
        shuffleButton.visibility = View.GONE
        loaded = 0
        okToLoad = false
        loadingImageView.visibility = View.VISIBLE
        doAsync {
            Glide.get(this@HomeScreen).clearDiskCache()
        }
        Glide.get(this@HomeScreen).clearMemory()
        manager.removeAndRecycleAllViews(recycleView.Recycler())
        adapter.clear()
        photos.clear()
        getEntries(key)
        checkOk()
    }

    fun calculateNoOfColumns(context: Context): Int {
        val displayMetrics = context.getResources().getDisplayMetrics();
        val dpWidth = displayMetrics.widthPixels / displayMetrics.density;
        val scalingFactor = 110 // You can vary the value held by the scalingFactor
        // variable. The smaller it is the more no. of columns you can display, and the
        // larger the value the less no. of columns will be calculated. It is the scaling
        // factor to tweak to your needs.
        var columnCount = (dpWidth / scalingFactor).toInt()
        if (columnCount < 3) columnCount = 3
        return columnCount // if column no. is less than 2, we still display 2 columns
    }

    fun getItemWidth(context: Context): Int {
        val displayMetrics = context.resources.displayMetrics
        val dpWidth = displayMetrics.widthPixels
        return (dpWidth / calculateNoOfColumns(context)).toInt()
    }
}

val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()

//Layout item for adapter
class PhotoItem(
    val cnt: Context,
    var url: String,
    var downloads: Int,
    var ref: DatabaseReference,
    var locked: Boolean
) : Item<ViewHolder>() {
    override fun bind(viewHolder: ViewHolder, position: Int) {
        viewHolder.itemView.itemImageView.layoutParams.width = itemWidth
        viewHolder.itemView.itemImageView.layoutParams.height = itemWidth
            Glide.with(cnt)
                .load(url)
                .apply(RequestOptions().placeholder(R.drawable.placeholder).format(DecodeFormat.PREFER_RGB_565))/*transition(DrawableTransitionOptions.withCrossFade(factory)).apply(RequestOptions().placeholder(R.drawable.background))*//**//*.skipMemoryCache(true)*//**//*priority(Priority.HIGH))*//**//*.listener(
            object : RequestListener<Bitmap> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: com.bumptech.glide.request.target.Target<Bitmap>?,
                    isFirstResource: Boolean
                ): Boolean {
                    return false
                }

                override fun onResourceReady(
                    resource: Bitmap?,
                    model: Any?,
                    target: Target<Bitmap>?,
                    dataSource: DataSource?,
                    isFirstResource: Boolean
                ): Boolean {
                    return false
                }
            })*/.into(viewHolder.itemView.itemImageView)
        viewHolder.itemView.itemImageView.transitionName = url
        viewHolder.itemView.setOnClickListener {
            var intent = Intent(cnt, PhotoScreen::class.java).apply {
                putExtra("url", url)
                putExtra("ref", ref.toString())
                putExtra("downs", downloads.toString())
                putExtra("locked", locked.toString())
            }
            val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                cnt as AppCompatActivity,
                viewHolder.itemView.itemImageView,
                viewHolder.itemView.itemImageView.transitionName
            )
            cnt.startActivity(intent, options.toBundle())
        }
    }

    override fun getLayout(): Int {
        return R.layout.image_item
    }

首页

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
        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:id="@+id/drawerLayoutMain"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".HomeScreen"
        android:background="@drawable/background">


    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent" android:id="@+id/relativeLayout"
    >


        <androidx.core.widget.NestedScrollView
                android:layout_width="0dp"
                android:layout_height="0dp"
                tools:layout_conversion_absoluteHeight="598dp"
                tools:layout_conversion_absoluteWidth="384dp" app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/toolbar"
                android:id="@+id/scrollView3"
                app:layout_constraintHorizontal_bias="1.0"
                app:layout_constraintBottom_toBottomOf="parent"
                android:overScrollMode="ifContentScrolls"
                android:fillViewport="false">
            <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
                          android:orientation="vertical" android:id="@+id/linearLayout"
                          android:descendantFocusability="blocksDescendants"
                          android:layout_marginLeft="0dp" android:layout_marginRight="0dp"
                          android:layout_gravity="fill">
                <androidx.recyclerview.widget.RecyclerView
                        android:layout_width="wrap_content"
                        android:layout_height="-100dp"
                        android:layout_marginTop="0dp" android:id="@+id/recycleView"
                        app:layout_behavior="@string/appbar_scrolling_view_behavior"
                        android:layout_marginRight="0dp"
                        android:focusableInTouchMode="true"
                        android:scrollbars="vertical"
                        android:layout_gravity="center_horizontal|fill"
                >
                </androidx.recyclerview.widget.RecyclerView>
                <pl.droidsonroids.gif.GifImageView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:id="@+id/waveImageView" android:src="@drawable/intro_image"
                        app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintBottom_toBottomOf="parent"
                        android:visibility="gone" android:background="@drawable/background"/>
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>
        <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="0dp"
                                           android:layout_height="45dp"
                                           android:background="@drawable/background"
                                           app:titleTextColor="@android:color/white"
                                           android:textAlignment="gravity"
                                           app:layout_constraintStart_toStartOf="parent"
                                           app:layout_constraintEnd_toEndOf="parent"
                                           android:isScrollContainer="false"
                                           android:fitsSystemWindows="false"
                                           tools:layout_conversion_absoluteHeight="42dp"
                                           tools:layout_conversion_absoluteWidth="384dp"
                                           app:layout_constraintTop_toTopOf="parent"
                                           app:navigationIcon="@drawable/ic_list_black_24dp">
        </androidx.appcompat.widget.Toolbar>
        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" app:srcCompat="@drawable/ic_shuffle_black_24dp"
                android:id="@+id/shuffleButton"
                app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="@+id/toolbar"
                android:padding="10dp"
                app:layout_constraintBottom_toTopOf="@+id/scrollView3"
                android:focusedByDefault="true"
        />
        <pl.droidsonroids.gif.GifImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/loadingImageView" android:visibility="visible"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp" android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent" android:src="@drawable/placeholder_gif"/>


    </androidx.constraintlayout.widget.ConstraintLayout>
    <com.google.android.material.navigation.NavigationView
            android:layout_width="250dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/header_menu"
            android:theme="@style/ThemeOverlay.AppCompat.ActionBar" android:id="@+id/navigView"
            app:itemBackground="@drawable/menu_item_background"
            android:background="@drawable/menu_bitmap"/>

</androidx.drawerlayout.widget.DrawerLayout>

ImageItem布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
>

    <ImageView
            android:layout_width="110dp"
            android:layout_height="110dp"
            android:id="@+id/itemImageView"
            app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
            android:scaleType="centerCrop"/>
</FrameLayout>
如我所见,

OnMeasure()和nativePollOnce()方法需要很多时间。还是不知道该怎么办

1 个答案:

答案 0 :(得分:0)

已解决

主要问题是 groupie适配器,目前在UI线程上产生差异。这造成了滞后。切换到另一个库可能会对您有所帮助。我已切换为 airbnb的环氧

P.S。我在滚动视图中嵌套了recyclerview。删除scrollview可以提高性能