阅读 376

Android利用WeakReference优化内存

今天看项目代码的时候发现了,一段代码引起了我的注意:

object DetailTopData {
    private val data = mutableMapOf<String, BookDetail>()
    fun setData(id: String, info: BookDetail){
        data[id] = info
    }

    fun getData(id: String): BookDetail?{
        return data[id]
    }
}

//存储值
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        findViewById<TextView>(R.id.normal).setOnClickListener {
            val book = BookDetail("wnq")
            DetailTopData.setData("A", book)
        }
    }
}
复制代码

这是一个多页面共享数据的工具类,A页面存储,B页面通过key获取,静态变量存储会造成所有存储进来的数据对象得不到释放,在内存吃紧的一些低端设备上无疑是雪上加霜。于是我就来改造下这段代码,首先想到的就是在用完共享数据之后就该允许GC回收掉它们,所以第一时间想到的是WeakReference,至于四大引用的基础知识就不在这里说明了,大家去找相应的博客学习吧。我修改后以及测试代码如下:

object DetailTopData {
    private val data = mutableMapOf<String, WeakReference<BookDetail>>()
    private val referenceQueue = ReferenceQueue<BookDetail>()
    fun setData(id: String, info: BookDetail){
        data[id] = WeakReference(info, referenceQueue)
    }

    fun getData(id: String): BookDetail?{
        return data[id]?.get()
    }

    fun start() {
        Thread{
            Log.w("WNQ", "开始检查回收")
            Log.w("WNQ", "data[A]: ${data["A"]?.get().toString()}")
            //阻塞,知道引用队列进来被回收的WeakReference
            val result = referenceQueue.remove()
            //打印这里是为了证明当引用队列收到WeakReference的时候,WeakReference包裹的对象已经被回收掉了
            Log.w("WNQ", "start: ${result.get().toString()}")
            Log.w("WNQ", "data[A]: ${data["A"]?.get().toString()}")
        }.start()
    }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        findViewById<TextView>(R.id.normal).setOnClickListener {
            val book = BookDetail("wnq")
            DetailTopData.setData("A", book)
            //开启引用队列检查
            DetailTopData.start()
        }
    }
}
复制代码

为了检测对象被回收了,使用弱引用和引用队列,ReferenceQueue的remove()是阻塞的,当没有消息进入队列的时候就会一直阻塞。下面开始测试,点击MainActivity中的按钮,往DetailTopData里面存储值,并开启线程检测引用队列是否有弱引用被回收。打印日志如下:

image.png 开始检查回收情况,并且打印内存中的对象信息,目前对象还是未被回收的状态。接下来手动触发GC:

image.png 然后我们查看日志:

image.png 触发了一次GC,对象被回收了,证明了WeakReference可以防止内存泄漏,但是上面的代码会出现一个问题,当我存储后还没来及的使用就被GC回收了,这样是不符合我们预期的,于是我们弱引用对象在社么条件下会被回收:“只有弱引用的对象才会被回收”,我们改写下代码:

class MainActivity : AppCompatActivity() {
    
    val book = BookDetail("wnq")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        findViewById<TextView>(R.id.normal).setOnClickListener {
            DetailTopData.setData("A", book)
            DetailTopData.start()
        }
    }
}
复制代码

MainActivity中持有一个强引用,也就是book目前存在一个强引用和一个弱引用,弱引用是用来和其他页面通信的,强引用是保持一个最短生命周期的。目前如此这样就会在退出MainActivity的时候才会回收掉存储的对象。不知道我说清楚了没,欢迎指正。

文章分类
Android
文章标签