欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android大圖監(jiān)測系統(tǒng)的三種實現(xiàn)方式

 更新時間:2024年01月03日 08:55:40   作者:午后一小憩  
在Android應用中,大圖的加載和顯示可能導致內(nèi)存占用過高,進而引發(fā)OOM(Out Of Memory)異常,影響應用的穩(wěn)定性和用戶體驗,為了更好地管理大圖資源,我們需要建立起一套可靠的大圖監(jiān)測系統(tǒng),文中有詳細的代碼示例供大家參考,需要的朋友可以參考下

原理解析

  • 內(nèi)存占用計算

首先,我們需要了解如何計算一張圖片在內(nèi)存中的占用大小。Android中,圖片占用的內(nèi)存主要由其寬、高和每個像素的位數(shù)決定。我們可以使用以下公式計算:

[ 內(nèi)存占用大小 = 寬 \times 高 \times 像素位數(shù) / 8 ]

  • 大圖判定標準

一般情況下,大圖的定義是指超過一定閾值的圖片。這個閾值可以根據(jù)應用的實際需求來設定,通常建議根據(jù)設備的內(nèi)存情況和應用場景動態(tài)調(diào)整。

  • 監(jiān)測策略

大圖監(jiān)測一般采用兩種策略:主動監(jiān)測被動監(jiān)測。主動監(jiān)測通過周期性地掃描內(nèi)存中的圖片資源,識別大圖,進行處理。而被動監(jiān)測則是在圖片加載過程中實時判斷是否為大圖。

主動監(jiān)測

主動監(jiān)測只要獲取到內(nèi)存中的圖片資源,通過掃描判斷是否超過設置的閾值即可。

class LargeImageScanner {

    fun scanLargeImages() {
        // 遍歷內(nèi)存中的圖片資源
        for (image in MemoryManager.getAllImages()) {
            val imageSize = calculateImageSize(image)
            
            // 判斷是否為大圖
            if (imageSize > LARGE_IMAGE_THRESHOLD) {
                // 進行處理,如壓縮、裁剪或異步加載
                handleLargeImage(image)
            }
        }
    }

    private fun calculateImageSize(image: Bitmap): Int {
        // 計算圖片占用的內(nèi)存大小
        return image.width * image.height * (image.config.bitsPerPixel / 8)
    }

    private fun handleLargeImage(image: Bitmap) {
        // 實現(xiàn)大圖的處理邏輯,例如壓縮、裁剪或異步加載
        // ...
    }
}

被動監(jiān)測

被動監(jiān)測的目的是,讓圖在加載的過程中,自動獲取到加載圖片的大小。所以切入的時機就非常重要。

在第三方圖片加載庫回調(diào)中進行大圖監(jiān)測

如果你使用的是第三方圖片加載庫Glide,最簡單的直接的是在圖片加載的成功的時機進行監(jiān)測。

class GlideImageLoader {

    fun loadWithLargeImageCheck(context: Context, url: String, target: ImageView) {
        Glide.with(context)
            .asBitmap()
            .load(url)
            .listener(object : RequestListener<Bitmap> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Bitmap>?,
                    isFirstResource: Boolean
                ): Boolean {
                    // 圖片加載失敗處理
                    // ...
                    return false
                }

                override fun onResourceReady(
                    resource: Bitmap?,
                    model: Any?,
                    target: Target<Bitmap>?,
                    dataSource: DataSource?,
                    isFirstResource: Boolean
                ): Boolean {
                    // 圖片加載成功,檢查是否為大圖
                    resource?.let {
                        val imageSize = calculateImageSize(it)
                        if (imageSize > LARGE_IMAGE_THRESHOLD) {
                            // 處理大圖邏輯,如壓縮、裁剪或異步加載
                            handleLargeImage(it)
                        }
                    }
                    return false
                }
            })
            .into(target)
    }

    private fun calculateImageSize(image: Bitmap): Int {
        // 計算圖片占用的內(nèi)存大小
        return image.width * image.height * (image.config.bitsPerPixel / 8)
    }

    private fun handleLargeImage(image: Bitmap) {
        // 實現(xiàn)大圖的處理邏輯,例如壓縮、裁剪或異步加載
        // ...
    }
}

但上面這種方式存在幾個弊端

  • 適用性低,強制要求所以圖片加載都要調(diào)用loadWithLargeImageCheck方法,如果是一個現(xiàn)有的大項目,將無法改造。
  • 強依賴于第三方加載庫Glide,后續(xù)換庫也不兼容

所以為了解決上面的這幾個問題,我們要想的是,能否不依賴于第三方圖片加載庫呢?

于是就有了下面這種方式

在網(wǎng)絡加載圖片時進行大圖監(jiān)測

現(xiàn)在使用網(wǎng)絡請求基本都是使用Okhttp,在這種情況下,你可以考慮使用攔截器(Interceptor)來實現(xiàn)通用的大圖監(jiān)測邏輯。攔截器是OkHttp 中的一種強大的機制,可以在請求發(fā)起和響應返回的過程中進行攔截、修改和監(jiān)測。

以下是一個使用OkHttp攔截器進行大圖監(jiān)測的示例:

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import java.io.IOException

class LargeImageInterceptor : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        // 發(fā)起請求前的處理,可以在這里記錄請求時間等信息

        val response = chain.proceed(request)

        // 請求返回后的處理
        if (response.isSuccessful) {
            val contentType = response.body()?.contentType()?.toString()

            // 檢查是否為圖片資源
            if (contentType?.startsWith("image/") == true) {
                // 獲取圖片大小并進行大圖監(jiān)測
                val imageSize = calculateImageSize(response.body()?.byteStream())
                if (imageSize > LARGE_IMAGE_THRESHOLD) {
                    // 處理大圖邏輯,如壓縮、裁剪或異步加載
                    handleLargeImage()
                }
            }
        }

        return response
    }

    private fun calculateImageSize(inputStream: InputStream?): Int {
        // 通過輸入流計算圖片占用的內(nèi)存大小
        // ...
    }

    private fun handleLargeImage() {
        // 實現(xiàn)大圖的處理邏輯,例如壓縮、裁剪或異步加載
        // ...
    }
}

然后,在創(chuàng)建OkHttpClient時,添加這個攔截器:

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(LargeImageInterceptor())
    .build()

通過這種方式,你只需要在OkHttp中添加一次攔截器,即可在每個圖片請求中進行通用的大圖監(jiān)測處理,而不用在每個請求的響應回調(diào)中添加監(jiān)測代碼。這樣使得代碼更加清晰、易于維護。

可能又有人會說,我網(wǎng)絡加載庫換了,那不是一樣無法兼容嗎?

確實,雖然概率比直接換第三方圖片加載庫還低,但既然有可能,就要盡可能的解決。

于是就是了下面的這種終極方法。

使用ASM插樁進行大圖監(jiān)控

這就升級到圖片加載的本質(zhì)了,任何圖片加載最終都是要填充到ImageView上。而在這過程中自然避免不了使用ImageView的方法進行填充圖片。

例如:setImageDrawable等等。

當然也可以直接hook整個ImageView,全局將其替換成HookImageView,再到其內(nèi)部實現(xiàn)大圖監(jiān)測。 這兩種都是通過ASM,只是對象不一樣,但原理都基本一致。

以下是一個簡單的示例,使用ASMAndroid中的 ImageViewsetImageDrawable 方法進行攔截:

import org.objectweb.asm.*;

public class ImageViewInterceptor implements ClassVisitor {

    private final ClassVisitor cv;

    public ImageViewInterceptor(ClassVisitor cv) {
        this.cv = cv;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        if (name.equals("setImageDrawable") && desc.equals("(Landroid/graphics/drawable/Drawable;)V")) {
            return new ImageViewMethodVisitor(mv);
        }
        return mv;
    }

    // 其他方法省略,你可以根據(jù)需要實現(xiàn)其他 visitX 方法
}

class ImageViewMethodVisitor extends MethodVisitor {

    public ImageViewMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitCode() {
        super.visitCode();
        // 在方法開頭插入大圖監(jiān)測邏輯的字節(jié)碼
        // ...
    }

    @Override
    public void visitInsn(int opcode) {
        if (opcode == Opcodes.RETURN) {
            // 在 RETURN 指令前插入大圖監(jiān)測邏輯的字節(jié)碼
            // ...
        }
        super.visitInsn(opcode);
    }
}

// 在某處,使用 ASM 進行字節(jié)碼修改
ClassReader cr = new ClassReader("android/widget/ImageView");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ImageViewInterceptor interceptor = new ImageViewInterceptor(cw);
cr.accept(interceptor, 0);

....

這個示例中,ImageViewInterceptorImageViewsetImageDrawable 方法進行了攔截,ImageViewMethodVisitor 中插入了大圖監(jiān)測邏輯的字節(jié)碼。

需要注意的是。在實際應用中,需謹慎考慮因字節(jié)碼操作而引起的潛在問題和兼容性風險。

注意事項與優(yōu)化技巧

在實現(xiàn)大圖監(jiān)測時,我們需要注意以下事項:

  • 靈活設置閾值: 根據(jù)不同設備和應用場景,動態(tài)調(diào)整大圖的閾值,以保證監(jiān)測的準確性和及時性。
  • 合理選擇處理方式: 對于大圖,可以選擇合適的處理方式,如壓縮、裁剪或異步加載,以降低內(nèi)存占用。
  • 異步處理: 將大圖的處理放在異步線程中,避免阻塞主線程,提高應用的響應性。

總結(jié)

通過本文的學習,相信你已經(jīng)對Android大圖監(jiān)測有了深入的理解,并可以在實際項目中應用這些知識,提升應用的性能和用戶體驗。

以上就是Android大圖監(jiān)測系統(tǒng)的三種實現(xiàn)方式的詳細內(nèi)容,更多關(guān)于Android大圖監(jiān)測系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論