Android Glide预处理preload原始图片到成品resource & 预加载RecyclerViewPreloader,Kotlin

Android Glide预处理preload原始图片到成品resource & 预加载RecyclerViewPreloader,Kotlin

XML 复制代码
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
XML 复制代码
    implementation 'com.github.bumptech.glide:glide:4.15.1'
    kapt 'com.github.bumptech.glide:compiler:4.15.1'
    implementation ("com.github.bumptech.glide:recyclerview-integration:4.15.1") {
        // Excludes the support library because it's already included by Glide.
        transitive = false
    }
Kotlin 复制代码
import android.content.Context
import android.util.Log
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
import com.bumptech.glide.load.engine.cache.LruResourceCache
import com.bumptech.glide.module.AppGlideModule


@GlideModule
class MyModule : AppGlideModule() {
    companion object {
        val mLruResourceCache = LruResourceCache(1024 * 1024 * 9999)
        val mLruBitmapPool = LruBitmapPool(1024 * 1024 * 9999)

        fun debug(msg: String) {
            Log.d(
                TAG,
                "$msg"
            )
        }
    }

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        builder.setMemoryCache(mLruResourceCache)
        builder.setBitmapPool(mLruBitmapPool)

        builder.setLogLevel(Log.DEBUG)
        Log.d(TAG, "自定义配置")

        super.applyOptions(context, builder)
    }

    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity" />

</RelativeLayout>
Kotlin 复制代码
import android.content.Context
import android.graphics.Bitmap
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.ListPreloader
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.util.FixedPreloadSizeProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

const val PHOTO_SIZE = 80
const val TAG = "MyGlide"
const val preloadSize = 100

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

        rvPreload()
    }

    private fun rvPreload() {
        val spanCount = 13

        var recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView?.layoutManager = GridLayoutManager(this, spanCount).apply {
            orientation = GridLayoutManager.VERTICAL
        }

        val adapter = MyAdapter(this)
        recyclerView?.adapter = adapter


        val heightCount = resources.displayMetrics.heightPixels / PHOTO_SIZE
        val max = 10 * heightCount * spanCount

        //如果没有句已加载且展示的格子再滑回去会出现残缺白块行。
        recyclerView.recycledViewPool.setMaxRecycledViews(0, max)

        CoroutineScope(Dispatchers.IO).launch {
            val items = readAllImage(applicationContext)

            //以len为长度值,将原始的列表切分为 [0-(len-1)],[len-列表结尾]两条。
            //第一条较短,快速完成原始图片预处理,紧急为用户快速展示图片(此时Glide将从resource直接装入内存)
            //第二条很长,适合在app冷启动后,后台静默加载。
            val len = 600
            val segment1 = items.subList(0, len)
            val segment2 = items.subList(len, items.size - 1)

            Log.d(TAG, "预处理 $len ...")
            segment1.forEachIndexed { index, myData ->
                glidePreload(myData)
            }
            Log.d(TAG, "预处理 $len")

            // 这段时间是拍脑瓜拍出来的。
            // Glide需要一定时间才能完成len个资源的预处理,把原始数据转换为resource成品。
            // 如果不等待不延迟,直接通知adapter更新数据,绝大多数情况下,Glide此时还没来得及完成预处理,没有预处理快速显示的效果。
            delay(10_000L)

            withContext(Dispatchers.Main) {
                Log.d(TAG, "预处理 $len done")
                adapter.onChange(items)
            }

            Log.d(TAG, "预处理 第二批 ...")
            segment2.forEachIndexed { index, myData ->
                glidePreload(myData)
            }
        }

        val preloadSizeProvider = FixedPreloadSizeProvider<MyData>(
            PHOTO_SIZE,
            PHOTO_SIZE
        )

        val preloadModelProvider = MyPreloadModelProvider(this, adapter)
        val preloader: RecyclerViewPreloader<MyData> = RecyclerViewPreloader(
            GlideApp.with(this),
            preloadModelProvider,
            preloadSizeProvider,
            preloadSize
        )

        recyclerView?.addOnScrollListener(preloader)
    }

    private fun glidePreload(myData: MyData) {
        //本身是线程化的,很快返回,不阻塞主线程。Glide将在后台线程中decode原始图片,加工成resource成品。
        GlideApp.with(this)
            .asBitmap()
            .load(myData.path)
            .centerCrop()
            .override(PHOTO_SIZE)
            .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
            .preload(PHOTO_SIZE, PHOTO_SIZE)
    }


    class MyPreloadModelProvider(
        private val ctx: Context,
        private val adapter: MyAdapter,
    ) : ListPreloader.PreloadModelProvider<MyData> {
        override fun getPreloadItems(position: Int): MutableList<MyData> {
            return mutableListOf(adapter.getItems()[position])
        }

        override fun getPreloadRequestBuilder(item: MyData): GlideRequest<Bitmap>? {
            return GlideApp.with(ctx)
                .asBitmap()
                .load(item.path)
                .centerCrop()
                .override(PHOTO_SIZE)
        }
    }

    class MyAdapter(private val ctx: Context) :
        RecyclerView.Adapter<MyViewHolder>() {
        private var items: MutableList<MyData>? = null

        fun onChange(items: MutableList<MyData>) {
            this.items = items
            notifyDataSetChanged()
        }

        fun getItems(): MutableList<MyData> {
            return items!!
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val view = LayoutInflater.from(ctx).inflate(R.layout.item, parent, false)
            val params = view.layoutParams
            params.width = PHOTO_SIZE
            params.height = PHOTO_SIZE
            return MyViewHolder(view)
        }

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            GlideApp.with(ctx)
                .asBitmap()
                .centerCrop()
                .override(PHOTO_SIZE, PHOTO_SIZE)
                .load(items?.get(position)?.path)
                .into(holder.image)

            holder.text.text = "$position"
        }

        override fun getItemCount(): Int {
            return items?.size ?: 0
        }

//        override fun getItemId(position: Int): Long {
//            return RecyclerView.NO_ID
//        }

        override fun getItemViewType(position: Int): Int {
            return 0
        }
    }

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val image: AppCompatImageView = itemView.findViewById(R.id.image)
        val text: TextView = itemView.findViewById(R.id.text)
    }

    private fun readAllImage(context: Context): ArrayList<MyData> {
        val photos = ArrayList<MyData>()

        //读取手机图片
        val cursor = context.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            null
        )
        var index = 0
        while (cursor!!.moveToNext()) {
            //图片路径 uri
            val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))

            //图片名称
            //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))

            //图片大小
            //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))

            photos.add(MyData(path, index++))
        }
        cursor.close()

        return photos
    }

    class MyData(var path: String, val index: Int)
}
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="1px">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher_background" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="-.-"
        android:textColor="@android:color/holo_red_dark"
        android:textSize="8dp" />
</RelativeLayout>

Android GlideApp FixedPreloadSizeProvider RecyclerViewPreloader,mix Java&Kotlin_zhangphil的博客-CSDN博客【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/131905329Android GlideApp GlideRequest FixedPreloadSizeProvider RecyclerViewPreloader,kotlin_zhangphil的博客-CSDN博客【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/131813200

相关推荐
alexhilton1 天前
面向开发者的系统设计:像建筑师一样思考
android·kotlin·android jetpack
CYRUS_STUDIO1 天前
用 Frida 控制 Android 线程:kill 命令、挂起与恢复全解析
android·linux·逆向
CYRUS_STUDIO1 天前
Frida 实战:Android JNI 数组 (jobjectArray) 操作全流程解析
android·逆向
用户092 天前
Gradle Cache Entries 深度探索
android·java·kotlin
循环不息优化不止2 天前
安卓 View 绘制机制深度解析
android
叽哥2 天前
Kotlin学习第 9 课:Kotlin 实战应用:从案例到项目
android·java·kotlin
雨白2 天前
Java 线程通信基础:interrupt、wait 和 notifyAll 详解
android·java
诺诺Okami2 天前
Android Framework-Launcher-UI和组件
android
潘潘潘2 天前
Android线程间通信机制Handler介绍
android
潘潘潘2 天前
Android动态链接库So的加载
android