Principle analysis
- Memory occupancy calculation
First, we need to understand how to calculate the size of a picture in memory. In Android, the memory occupied by the image is mainly determined by its width, height and the number of bits per pixel. We can use the following formula to calculate:
[Memory footprint = width \times High \times Pixel bit count / 8 ]
- Large picture judgment criteria
Generally speaking, the definition of a large picture refers to a picture that exceeds a certain threshold. This threshold can be set according to the actual needs of the application, and it is usually recommended to dynamically adjust it according to the memory conditions of the device and application scenarios.
- Monitoring strategies
Large picture monitoring generally adopts two strategies:Active monitoringandPassive monitoring. Actively monitor and periodically scan image resources in memory, identify large images, and process them. Passive monitoring is to determine whether it is a large image in real time during the image loading process.
Active monitoring
Active monitoring is enough to obtain the image resources in memory and determine whether the set threshold is exceeded by scanning.
class LargeImageScanner { fun scanLargeImages() { // traverse the image resources in memory for (image in ()) { val imageSize = calculateImageSize(image) // Determine whether it is a large picture if (imageSize > LARGE_IMAGE_THRESHOLD) { // Process, such as compression, cropping, or asynchronous loading handleLargeImage(image) } } } private fun calculateImageSize(image: Bitmap): Int { // Calculate the memory size occupied by the picture return * * ( / 8) } private fun handleLargeImage(image: Bitmap) { // Implement large-picture processing logic, such as compression, cropping or asynchronous loading // ... } }
Passive monitoring
The purpose of passive monitoring is to allow the image to automatically obtain the size of the loaded image during the loading process. So the timing of entering is very important.
Large image monitoring in third-party image loading library callback
If you are using a third-party image loading libraryGlide
, the easiest and straightforward thing is to monitor the time of successful image loading.
class GlideImageLoader { fun loadWithLargeImageCheck(context: Context, url: String, target: ImageView) { (context) .asBitmap() .load(url) .listener(object : RequestListener<Bitmap> { override fun onLoadFailed( e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean ): Boolean { // Image loading failed processing // ... return false } override fun onResourceReady( resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean ): Boolean { // The image is loaded successfully, check whether it is a large image resource?.let { val imageSize = calculateImageSize(it) if (imageSize > LARGE_IMAGE_THRESHOLD) { // Handle large image logic such as compression, cropping or asynchronous loading handleLargeImage(it) } } return false } }) .into(target) } private fun calculateImageSize(image: Bitmap): Int { // Calculate the memory size occupied by the picture return * * ( / 8) } private fun handleLargeImage(image: Bitmap) { // Implement large-picture processing logic, such as compression, cropping or asynchronous loading // ... } }
But the above method has several disadvantages
- Low applicability, mandatory, so the image loading needs to be called
loadWithLargeImageCheck
Method, if it is an existing large project, it will not be remodeled. - Rely on third-party loading libraries
Glide
, subsequent library replacement is not compatible
So in order to solve the above problems, what we need to think about is whether we can not rely on third-party image loading libraries?
So the following method
Large image monitoring when loading pictures on the network
Now, network requests are basically usedOkhttp
, In this case, you can consider using an interceptor to implement general large-picture monitoring logic. The interceptor isOkHttp
A powerful mechanism in which intercepts, modifys and monitors during request initiation and response return.
The following is a useOkHttp
Example of interceptors performing large-picture monitoring:
import import import import class LargeImageInterceptor : Interceptor { @Throws(IOException::class) override fun intercept(chain: ): Response { val request = () // For processing before initiating a request, you can record the request time and other information here. val response = (request) // Processing after the request returns if () { val contentType = ()?.contentType()?.toString() // Check whether it is an image resource if (contentType?.startsWith("image/") == true) { // Obtain the image size and perform large image monitoring val imageSize = calculateImageSize(()?.byteStream()) if (imageSize > LARGE_IMAGE_THRESHOLD) { // Handle large image logic such as compression, cropping or asynchronous loading handleLargeImage() } } } return response } private fun calculateImageSize(inputStream: InputStream?): Int { // Calculate the memory size occupied by the image through the input stream // ... } private fun handleLargeImage() { // Implement large-picture processing logic, such as compression, cropping or asynchronous loading // ... } }
Then, in CreateOkHttpClient
When adding this interceptor:
val okHttpClient = () .addInterceptor(LargeImageInterceptor()) .build()
In this way, you just need toOkHttp
Add an interceptor once to perform general large image monitoring processing in each image request without adding monitoring code to each request response callback. This makes the code clearer and easier to maintain.
Some people may say that if my network loading library has been replaced, isn’t it the same incompatible?
Indeed, although the probability is lower than directly changing to a third-party image loading library, since it is possible, it must be solved as much as possible.
So the following ultimate method is achieved.
Use ASM Insertion for Large Picture Monitoring
This is upgraded to the essence of image loading. Any image loading will eventually be filled inImageView
superior. And naturally, it is impossible to avoid using it in this processImageView
method to fill the picture.
For example:setImageDrawable
etc.
Of course it can also be directlyhook
entireImageView
, replace it globally withHookImageView
, and then implement large-scale monitoring within it. Both of these are throughASM
, but the objects are different, but the principles are basically the same.
Here is a simple example usingASM
rightAndroid
In-houseImageView
ofsetImageDrawable
Method to intercept:
import .*; public class ImageViewInterceptor implements ClassVisitor { private final ClassVisitor cv; public ImageViewInterceptor(ClassVisitor cv) { = cv; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = (access, name, desc, signature, exceptions); if (("setImageDrawable") && ("(Landroid/graphics/drawable/Drawable;)V")) { return new ImageViewMethodVisitor(mv); } return mv; } // Other methods are omitted, you can implement other visitX methods as needed} class ImageViewMethodVisitor extends MethodVisitor { public ImageViewMethodVisitor(MethodVisitor mv) { super(Opcodes.ASM5, mv); } @Override public void visitCode() { (); // Insert the bytecode of the large image monitoring logic at the beginning of the method // ... } @Override public void visitInsn(int opcode) { if (opcode == ) { // Insert the bytecode of the large image monitoring logic before the RETURN instruction // ... } (opcode); } } // Somewhere, use ASM to modify bytecodeClassReader cr = new ClassReader("android/widget/ImageView"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ImageViewInterceptor interceptor = new ImageViewInterceptor(cw); (interceptor, 0); ....
In this example,ImageViewInterceptor
rightImageView
ofsetImageDrawable
The method intercepted,ImageViewMethodVisitor
The bytecode of the large image monitoring logic is inserted.
It should be noted. In practical applications, potential problems and compatibility risks arising from bytecode operations should be carefully considered.
Notes and optimization techniques
When implementing large-picture monitoring, we need to pay attention to the following things:
- Flexible setting of thresholds:According to different equipment and application scenarios, the threshold of the large map is dynamically adjusted to ensure the accuracy and timeliness of monitoring.
- Reasonably choose the processing method:For large images, you can choose appropriate processing methods such as compression, cropping, or asynchronous loading to reduce memory usage.
- Asynchronous processing:Place the processing of large images in asynchronous threads to avoid blocking the main thread and improve the responsiveness of the application.
Summarize
Through the study of this article, I believe you have a deep understanding of Android large image monitoring and can apply this knowledge in actual projects to improve the performance and user experience of the application.
The above is the detailed content of the three implementation methods of Android large image monitoring system. For more information about Android large image monitoring system, please pay attention to my other related articles!