今天看项目代码的时候发现了,一段代码引起了我的注意:
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里面存储值,并开启线程检测引用队列是否有弱引用被回收。打印日志如下:
开始检查回收情况,并且打印内存中的对象信息,目前对象还是未被回收的状态。接下来手动触发GC:
然后我们查看日志:
触发了一次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的时候才会回收掉存储的对象。不知道我说清楚了没,欢迎指正。