文章目录

  • 一、Bitmap 复用池
  • 二、弱引用 Bitmap 内存释放
  • 三、从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象
    • 1、Android 2.3.3(API 级别 10)及以下的版本
    • 2、Android 4.4(API 级别 19)以下的版本
    • 2、在 Android 4.4(API 级别 19)及以上的版本
  • 四、LruCache 内存缓存、内存复用工具类
    • 1、工具类
    • 2、工具类测试
    • 3、执行结果
  • 五、源码及资源下载

在上一篇博客 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 缓存策略 | LruCache 内存缓存 | LruCache 常用操作 | 工具类代码 ) 中 , 使用 LruCache 缓存 Bitmap 数据到内存中 , 设置其最大缓存为应用可用内存的 1/8 , 将解码后的 Bitmap 对象缓存到 LruCache 中 , 避免重复使用该 Bitmap 对象时重复解码加载图片 ;

一、Bitmap 复用池


1 . Bitmap 复用池 : 加载图片时 , 使用 inBitmap 复用选项 , 需要获取图片时 , 优先从 Bitmap 复用池中查找复用已存在的 Bitmap 对象 ; 假如 Bitmap 对象长时间不使用 , 就会从 LruCache 内存缓存中移除 , 此时放入到 Bitmap 复用池中 ;

2 . 弱引用 : 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收没有被引用的 Bitmap , 需要创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用 ;

Set<WeakReference<Bitmap>> bitmapReusePool;

二、弱引用 Bitmap 内存释放


有一点特别注意 , Java 中的弱引用 , 在 GC 时会回收没有使用到的内存 ; Bitmap 内存如果在 Java 层 , 可以将该内存回收 , 但是如果 Bitmap 内存在 Native 层 , 必须调用 Bitmap 对象的 recycle 方法 , 才能将内存释放 ;

1 . Bitmap 内存放置策略 :

  • 3.0 以下系统中 , Bitmap 内存在 Native 层
  • 3.0 以上系统中 , Bitmap 内存在 Java 层
  • 8.0 及以上的系统中 , Bitmap 内存在 Native 层

为了适配所有手机 , 所有版本 , 不管 GC 是否自动释放 Bitmap 内存 , 在弱引用对象被回收时 , 必须手动调用一下 Bitmap 对象的 recycle 方法 ;

2 . 兼容弱引用释放方法 : 使用引用队列 ReferenceQueue 监控该弱引用 Bitmap 的 Set 集合元素 , 当有 Bitmap 被回收后 , 就会将其放入 ReferenceQueue 中 , 此时开启一个线程 , 不断从 ReferenceQueue 调用 remove 方法获取被释放的内存对象 , 如果获取到了非空内容 , 说明有一个 Bitmap 弱引用对象被释放了 , 拿到该对象引用 Reference 后 , 获取其对应的 Bitmap 对象 , 手动调用 Bitmap 对象的 recycle 方法 , 即可完成对应操作 ;

代码示例 :

 /*** Bitmap 复用池* 使用 inBitmap 复用选项* 需要获取图片时 , 优先从 Bitmap 复用池中查找* 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收该 Bitmap* 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用** 该 Bitmap 复用池的作用是 , 假如 Bitmap 对象长时间不使用 , 就会从内存缓存中移除** 因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了* 需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存** 需要使用引用队列监控弱引用的释放情况*/Set<WeakReference<Bitmap>> bitmapReusePool;/*** 引用队列 , 用于监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收* 需要维护一个线程 , 不断尝试从该引用队列中获取引用**/private ReferenceQueue<Bitmap> referenceQueue;/*** 监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收 ,* 调用 ReferenceQueue<Bitmap> referenceQueue 的 remove 方法 ,* 查看是否存在被回收的弱引用 , 如果存在 , 直接回收该弱引用对应的 Bitmap 对象*/private Thread referenceQueueMonitorThread;/*** 是否持续监控引用队列 ReferenceQueue*/private boolean isMonitorReferenceQueue = true;/*** 初始化引用队列*/private void initBitmapReusePool(){// 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用bitmapReusePool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());// 引用队列 , 当弱引用被 GC 扫描后 , 需要回收 , 会将该弱引用放入队列// 一直不断的尝试从该引用队列中获取数据 , 如果获取到数据 , 就要回收该对象referenceQueue = new ReferenceQueue<>();// 定义监控线程referenceQueueMonitorThread = new Thread(){@Overridepublic void run() {while (isMonitorReferenceQueue){try {Reference<Bitmap> reference = (Reference<Bitmap>) referenceQueue.remove();Bitmap bitmap = reference.get();// 不为空 , 且没有被回收 , 回收 Bitmap 内存if(bitmap != null && !bitmap.isRecycled()){bitmap.recycle();}} catch (InterruptedException e) {e.printStackTrace();}}}};// 启动引用队列监控线程referenceQueueMonitorThread.start();}

三、从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象


根据不同系统版本进行不同处理 :

1、Android 2.3.3(API 级别 10)及以下的版本

Android 2.3.3(API 级别 10)及以下的版本 : 使用 Bitmap 对象的 recycle 方法回收内存 ;

        // Android 2.3.3(API 级别 10)及以下的版本中 , 使用 Bitmap 对象的 recycle 方法回收内存if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1){// 如果 API 级别小于等于 10 , 不启用 Bitmap 内存复用机制 , 返回 null 即可return null;}

2、Android 4.4(API 级别 19)以下的版本

Android 4.4(API 级别 19)以下的版本 : 复用的前提是必须同时满足以下 3 个条件 :

  • 被解码的图像必须是 JPEG 或 PNG 格式
  • 被复用的图像宽高必须等于 解码后的图像宽高
  • 解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放

才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的 BitmapFactory.Options.inPreferredConfig 参数 ;

             if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2){/*Android 4.4(API 级别 19)以下的版本 : 在 Android 4.4(API 级别 19) 之前的代码中 ,复用的前提是必须同时满足以下 3 个条件 :1. 被解码的图像必须是 JPEG 或 PNG 格式2. 被复用的图像宽高必须等于 解码后的图像宽高3. 解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的BitmapFactory.Options.inPreferredConfig 参数 ;*/if(bitmap.getWidth() == width &&bitmap.getHeight() == height && //被复用的图像宽高必须等于 解码后的图像宽高inSampleSize == 1){// 图像的 BitmapFactory.Options.inSampleSize 设置为 1//符合要求inBitmap = bitmap;iterator.remove();}}

2、在 Android 4.4(API 级别 19)及以上的版本

在 Android 4.4(API 级别 19)及以上的版本 : 只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ; 解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;

                }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){/*在 Android 4.4(API 级别 19)及以上的版本中 ,只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;*/// 首先要计算图像的内存占用 , 先要计算出图像的宽高 , 如果图像需要缩放 , 计算缩放后的宽高if(inSampleSize > 1){width = width / inSampleSize ;height = height / inSampleSize;}// 计算内存占用 , 默认 ARGB_8888 格式int byteInMemory = width * height * 4;;if(bitmap.getConfig() == Bitmap.Config.ARGB_8888){// 此时每个像素占 4 字节byteInMemory = width * height * 4;}else if(bitmap.getConfig() == Bitmap.Config.RGB_565){// 此时每个像素占 2 字节byteInMemory = width * height * 2;}// 如果解码后的图片内存小于等于被复用的内存大小 , 可以复用if(byteInMemory <= bitmap.getAllocationByteCount()){//符合要求inBitmap = bitmap;iterator.remove();}}

四、LruCache 内存缓存、内存复用工具类


1、工具类

BitmapLruCacheMemoryReuse.java 工具类地址 : BitmapLruCacheMemoryReuse.java

package kim.hsl.bm.utils;import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.LruCache;import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;/*** Bitmap 内存缓存* 在将图片缓存到 LruCache 内存中基础上 ,* 将从 LruCache 中移除的最近没有使用的 Bitmap 对象的内存复用* 这样能最大限度减少内存抖动*/
public class BitmapLruCacheMemoryReuse {private static final String TAG = "BitmapMemoryCache";/*** 应用上下文对象*/private Context mContext;/*** 缓存图片的 LruCache*/private LruCache<String, Bitmap> mLruCache;/*** Bitmap 复用池* 使用 inBitmap 复用选项* 需要获取图片时 , 优先从 Bitmap 复用池中查找* 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收该 Bitmap* 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用** 该 Bitmap 复用池的作用是 , 假如 Bitmap 对象长时间不使用 , 就会从内存缓存中移除** Bitmap 回收策略 :* 3.0 以下系统中 , Bitmap 内存在 Native 层* 3.0 以上系统中 , Bitmap 内存在 Java 层* 8.0 及以上的系统中 , Bitmap 内存在 Native 层** 因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了* 需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存** 需要使用引用队列监控弱引用的释放情况*/Set<WeakReference<Bitmap>> bitmapReusePool;/*** 引用队列 , 用于监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收* 需要维护一个线程 , 不断尝试从该引用队列中获取引用**/private ReferenceQueue<Bitmap> referenceQueue;/*** 监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收 ,* 调用 ReferenceQueue<Bitmap> referenceQueue 的 remove 方法 ,* 查看是否存在被回收的弱引用 , 如果存在 , 直接回收该弱引用对应的 Bitmap 对象*/private Thread referenceQueueMonitorThread;/*** 是否持续监控引用队列 ReferenceQueue*/private boolean isMonitorReferenceQueue = true;/*** 单例实现*/private static BitmapLruCacheMemoryReuse INSTANCE;private BitmapLruCacheMemoryReuse(){}public static BitmapLruCacheMemoryReuse getInstance(){if(INSTANCE == null){INSTANCE = new BitmapLruCacheMemoryReuse();}return INSTANCE;}/*** 使用时初始化* @param context*/public void init(Context context){// 初始化内存缓存initLruCache(context);// 初始化弱引用队列initBitmapReusePool();}/*** 不使用时释放*/public void release(){isMonitorReferenceQueue = false;}private void initLruCache(Context context){// 为成员变量赋值this.mContext = context;// 获取 Activity 管理器ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);// 获取应用可用的最大内存int maxMemory = activityManager.getMemoryClass();// 获取的 maxMemory 单位是 MB , 将其转为字节 , 除以 8int lruCacheMemoryByte = maxMemory / 8 * 1024 * 1024;// 设置的内存 , 一般是 APP 可用内存的 1/8mLruCache = new LruCache<String, Bitmap>(lruCacheMemoryByte){/*** 返回 LruCache 的键和值的大小 , 单位使用用户自定义的单位* 默认的实现中 , 返回 1 ; size 是 键值对个数 , 最大的 size 大小是最多键值对个数* 键值对条目在 LruCache 中缓存时 , 其大小不能改变* @param key* @param value* @return 返回 LruCache<String, Bitmap> 的值 , 即 Bitmap 占用内存*/@Overrideprotected int sizeOf(String key, Bitmap value) {// 如果使用的是复用的 Bitmap 对象 , 其占用内存大小是之前的图像分配的内存大小// 大于等于当前图像的内存占用大小if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {return value.getAllocationByteCount();}return value.getByteCount();}/*** 从 LruCache 缓存移除 Bitmap 时会回调该方法* @param evicted* @param key* @param oldValue* @param newValue*/@Overrideprotected void entryRemoved(boolean evicted, String key, Bitmap oldValue,Bitmap newValue) {super.entryRemoved(evicted, key, oldValue, newValue);/*如果从 LruCache 内存缓存中移除的 Bitmap 是可变的才能被复用 , 否则只能回收该 Bitmap 对象Bitmap 回收策略 :3.0 以下系统中 , Bitmap 内存在 Native 层3.0 以上系统中 , Bitmap 内存在 Java 层8.0 及以上的系统中 , Bitmap 内存在 Native 层因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存*/if(oldValue.isMutable()){   // 可以被复用// 将其放入弱引用中 , 每次 GC 启动后 , 如果该弱引用没有被使用 , 都会被回收bitmapReusePool.add(new WeakReference<Bitmap>(oldValue, referenceQueue));}else{  // 不可被复用 , 直接回收oldValue.recycle();}}};}private void initBitmapReusePool(){// 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用bitmapReusePool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());// 引用队列 , 当弱引用被 GC 扫描后 , 需要回收 , 会将该弱引用放入队列// 一直不断的尝试从该引用队列中获取数据 , 如果获取到数据 , 就要回收该对象referenceQueue = new ReferenceQueue<>();// 定义监控线程referenceQueueMonitorThread = new Thread(){@Overridepublic void run() {while (isMonitorReferenceQueue){try {Reference<Bitmap> reference = (Reference<Bitmap>) referenceQueue.remove();Bitmap bitmap = reference.get();// 不为空 , 且没有被回收 , 回收 Bitmap 内存if(bitmap != null && !bitmap.isRecycled()){bitmap.recycle();}} catch (InterruptedException e) {e.printStackTrace();}}}};// 启动引用队列监控线程referenceQueueMonitorThread.start();}/*** 获取一个可以被复用的 Bitmap 对象** 与 BitmapFactory 配合使用 :** Android 4.4 以后的 Bitmap 复用情况 :* 在 KITKAT ( Android 4.4 , 19 平台 ) 以后的代码中 ,* 只要被解码生成的 Bitmap 对象的字节大小 ( 缩放后的 )* 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;** Android 4.4 之前的 Bitmap 复用情况 : ( 比较苛刻 )* 在 KITKAT 之前的代码中 , 被解码的图像必须是*  - JPEG 或 PNG 格式 ,*  - 并且 图像大小必须是相等的 ,*  - inssampleSize 设置为 1 ,* 才能复用成功 ;* 另外被复用的图像的 像素格式 Config ( 如 RGB_565 ) 会覆盖设置的 inPreferredConfig 参数** @param width* @param height* @param inSampleSize* @return*/public Bitmap getReuseBitmap(int width,int height,int inSampleSize){// Android 2.3.3(API 级别 10)及以下的版本中 , 使用 Bitmap 对象的 recycle 方法回收内存if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1){// 如果 API 级别小于等于 10 , 不启用 Bitmap 内存复用机制 , 返回 null 即可return null;}// 获取准备复用的 Bitmap , 之后设置到 Options 中Bitmap inBitmap = null;// 使用迭代器遍历该 Set 集合 , 如果遍历中涉及到删除 , 就要使用迭代器遍历Iterator<WeakReference<Bitmap>> iterator = bitmapReusePool.iterator();//迭代查找符合复用条件的Bitmapwhile (iterator.hasNext()){// 循环遍历 Bitmap 对象Bitmap bitmap = iterator.next().get();if (bitmap != null){/*检查该 Bitmap 对象是否可以达到复用要求 ,如果达到复用要求 , 就取出这个 Bitmap 对象 , 并将其从队列中移除*/if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2){/*Android 4.4(API 级别 19)以下的版本 : 在 Android 4.4(API 级别 19) 之前的代码中 ,复用的前提是必须同时满足以下 3 个条件 :1. 被解码的图像必须是 JPEG 或 PNG 格式2. 被复用的图像宽高必须等于 解码后的图像宽高3. 解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的BitmapFactory.Options.inPreferredConfig 参数 ;*/if(bitmap.getWidth() == width &&bitmap.getHeight() == height && //被复用的图像宽高必须等于 解码后的图像宽高inSampleSize == 1){// 图像的 BitmapFactory.Options.inSampleSize 设置为 1//符合要求inBitmap = bitmap;iterator.remove();}}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){/*在 Android 4.4(API 级别 19)及以上的版本中 ,只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;*/// 首先要计算图像的内存占用 , 先要计算出图像的宽高 , 如果图像需要缩放 , 计算缩放后的宽高if(inSampleSize > 1){width = width / inSampleSize ;height = height / inSampleSize;}// 计算内存占用 , 默认 ARGB_8888 格式int byteInMemory = width * height * 4;;if(bitmap.getConfig() == Bitmap.Config.ARGB_8888){// 此时每个像素占 4 字节byteInMemory = width * height * 4;}else if(bitmap.getConfig() == Bitmap.Config.RGB_565){// 此时每个像素占 2 字节byteInMemory = width * height * 2;}// 如果解码后的图片内存小于等于被复用的内存大小 , 可以复用if(byteInMemory <= bitmap.getAllocationByteCount()){//符合要求inBitmap = bitmap;iterator.remove();}}}else if( bitmap == null ){// 如果 bitmap 为空 , 直接从复用 Bitmap 集合中移除iterator.remove();}}return inBitmap;}/*下面的 3 个方法是提供给用户用于操作 LruCache 的接口*//*** 将键值对放入 LruCache 中* @param key* @param value*/public void putBitmapToLruCache(String key, Bitmap value){mLruCache.put(key, value);}/*** 从 LruCache 中获取 Bitmap 对象* @param key* @return*/public Bitmap getBitmapFromLruCache(String key){return mLruCache.get(key);}/*** 清除 LruCache 缓存*/public void clearLruCache(){mLruCache.evictAll();}
}

2、工具类测试

package kim.hsl.bm;import androidx.appcompat.app.AppCompatActivity;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;import kim.hsl.bm.utils.BitmapLruCacheMemoryReuse;
import kim.hsl.bm.utils.BitmapSizeReduce;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.sample_text);tv.setText(stringFromJNI());// 内存缓存memoryCache();}/*** 图像缓存*/private void memoryCache(){// 初始化 LruCache 内存缓存 , 与引用队列 , 一般在 onCreate 方法中初始化// 这里为了演示 , 放在方法的开头位置BitmapLruCacheMemoryReuse.getInstance().init(this);// 第一次从 LruCache 内存中获取 Bitmap 数据Bitmap bitmap = BitmapLruCacheMemoryReuse.getInstance().getBitmapFromLruCache(R.drawable.blog + "");/*如果从内存中获取 Bitmap 对象失败 , 这里就需要创建该图片 , 并放入 LruCache 内存中*/if(bitmap == null){// 要复用内存的 Bitmap 对象 , 将新的 Bitmap 写入到该 Bitmap 内存中Bitmap inBitmap = null;// 尝试获取复用对象BitmapLruCacheMemoryReuse.getInstance().getReuseBitmap(200, 200, 1);// 加载指定大小格式的图像bitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog,200, 200, false, inBitmap);// 将新的 bitap 放入 LruCache 内存缓存中BitmapLruCacheMemoryReuse.getInstance().putBitmapToLruCache(R.drawable.blog + "", bitmap);Log.i("Bitmap 没有获取到创建新的", "blog : " + bitmap.getWidth() + " , " +bitmap.getHeight() + " , " +bitmap.getByteCount());}else{Log.i("Bitmap 内存中获取数据", "blog : " + bitmap.getWidth() + " , " +bitmap.getHeight() + " , " +bitmap.getByteCount());}// 第一次从 LruCache 内存中获取 Bitmap 数据Bitmap bitmap2 = BitmapLruCacheMemoryReuse.getInstance().getBitmapFromLruCache(R.drawable.blog + "");Log.i("Bitmap 第二次内存中获取数据", "blog : " + bitmap2.getWidth() + " , " +bitmap2.getHeight() + " , " +bitmap2.getByteCount());}

3、执行结果

执行结果 : 第一次尝试从 LruCache 中获取图像 , 没有获取到 , 创建新的 Bitmap 放入 LruCache 中 , 第二次获取直接从 LruCache 中获取到了图像 ;

2020-07-02 15:15:48.300 5133-5133/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap options.outWidth=1990 , options.outHeight=1020
2020-07-02 15:15:48.300 5133-5133/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap inSampleSize=16
2020-07-02 15:15:48.327 5133-5133/kim.hsl.bm I/Bitmap 没有获取到创建新的: blog : 124 , 63 , 15624
2020-07-02 15:15:48.328 5133-5133/kim.hsl.bm I/Bitmap 第二次内存中获取数据: blog : 124 , 63 , 15624

五、源码及资源下载


源码及资源下载地址 :

  • ① GitHub 工程地址 : BitmapMemory

  • ② BitmapLruCacheMemoryReuse.java 工具类地址 : BitmapLruCacheMemoryReuse.java

【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )相关推荐

  1. 解密android日志xlog,安卓开发技巧2:自定义日志工具类XLog的实现

    安卓开发技巧二:自定义日志工具类XLog的实现 我们在开发过程中,打印日志是必不可少的一个调试环节,然而,直接使用系统自带的Log日志类,并不能满足我们实际项目的需求:假如我们现在在开发一款比较大的项 ...

  2. android 软键盘工具类,Android开发之弹出软键盘工具类简单示例

    本文实例讲述了Android开发之弹出软键盘工具类.分享给大家供大家参考,具体如下: package com.maobang.imsdk.util; import android.content.Co ...

  3. 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 缓存策略 | LruCache 内存缓存 | LruCache 常用操作 | 工具类代码 )

    文章目录 一.Bitmap 内存缓存策略 二.LruCache 内存缓存 三.LruCache 常用操作 四.LruCache 工具类 五.源码及资源下载 官方参考 : Google 官方提供的 内存 ...

  4. Android系统性能优化(69)---含内存优化、布局优化

    Android性能优化:含内存优化.布局优化 前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优 ...

  5. Android 内存优化实操,定位内存问题

    文章目录 一.内存泄漏定位 1.观察法: 2.使用内存分析工具 2-1.收集内存快照 2-2.hprof文件转换 2-3.Mat分析内存 二.内存抖动 三.优化内存空间 1.减少不必要的内存开销 2. ...

  6. Google官方 详解 Android 性能优化【史诗巨著之内存篇】

    尊重博主原创,如需转载,请附上本文链接http://blog.csdn.net/chivalrousman/article/details/51553114#t16 为什么关注性能 对于一款APP,用 ...

  7. 【Android 内存优化】垃圾回收算法 ( 内存优化总结 | 常见的内存泄漏场景 | GC 算法 | 标记清除算法 | 复制算法 | 标记压缩算法 )

    文章目录 一. 内存优化总结 二. 常见的内存泄漏场景 三. 内存回收算法 四. 标记-清除算法 ( mark-sweep ) 五. 复制算法 六. 标记-压缩算法 一. 内存优化总结 内存泄漏原理 ...

  8. 安全管家安卓_内存优化管家v1.0下载-内存优化管家app最新版下载

    内存优化管家是一款安卓手机多功能清理工具,包含了文件垃圾.缓存垃圾.广告垃圾和内存垃圾等分类清理功能,使用方法简单,一键扫描手机,即可进行不同类型的垃圾划分, 用户可以根据需求进行筛选清理,除此之外, ...

  9. java内存优化详解_jvm堆内存优化详解

    在日常的运维工作中用到tomcat,都需要对tomcat中的jvm虚拟机进行优化,只有知道需要优化参数的具体用处,才能深刻体会优化jvm的意义所在. 在平常的工作中我们谈对jvm的优化,主要是针对ja ...

最新文章

  1. axure 模板_《惢客创业日记》2019.09.03(周二) 用Axure管理项目流程
  2. Udacity机器人软件工程师课程笔记(三十) - 语义分割与实例实现 - 使用keras实现语义分割
  3. keras神经网络回归预测_如何使用Keras建立您的第一个神经网络来预测房价
  4. 怎么选?阿里P7 or 副处级干部?
  5. Android总结 之 View系统学习(一)
  6. P4342:[IOI1998]Polygon(区间dp)
  7. Redis免费客户端 Another Redis DeskTop Manager 下载地址及安装教程
  8. Magento创建多语言店铺的方法
  9. 12-1。。。面试的一些体会吧
  10. 微服务系列 —— 一小时搞定Eureka
  11. 李炎恢老师Dreamweaver视频课程(53课时)[已完结]
  12. GitGithub学习笔记
  13. jfinal 源码中文乱码解决
  14. 浅析互联网行业套路玩法+攻略
  15. ValueError: Variable in_hidden/weights already exists, disallowed. Did you mean to set reuse=True or
  16. Aliyun 阿里云 机器翻译调用 详解
  17. android 静态注册获取电量,获取手机电池百分比和电池容量方法
  18. idea复制项目并使用技巧
  19. Python入门到精通(4):关系与逻辑表达式
  20. 怎么保证促销商品不会超卖

热门文章

  1. C# 对象深拷贝、浅铐贝、直接拷贝(转)
  2. 用MS SQL 语句修改表中字段类型
  3. CriminalIntent项目开发--后篇
  4. UESTC 电子科大专题训练 数据结构 J
  5. 最完美的xslt数值函数与字符串函数(转)
  6. Javascript之学习笔记每日更新
  7. Linux 高性能服务器编程——socket选项
  8. weboffice 应用
  9. 也跟90后小朋友聊聊
  10. 微信小程序实现图片是上传、预览功能