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

相关推荐
恋猫de小郭2 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin
aqi002 小时前
FFmpeg开发笔记(七十七)Android的开源音视频剪辑框架RxFFmpeg
android·ffmpeg·音视频·流媒体
androidwork4 小时前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
梦天20155 小时前
android核心技术摘要
android
szhangbiao6 小时前
“开发板”类APP如果做屏幕适配
android
高林雨露7 小时前
RecyclerView中跳转到最后一条item并确保它在可视区域内显示
android
移动开发者1号10 小时前
ReLinker优化So库加载指南
android·kotlin
山野万里__10 小时前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
Huckings10 小时前
Android 性能问题
android
移动开发者1号10 小时前
剖析 Systrace:定位 UI 线程阻塞的终极指南
android·kotlin