Skip to content

Commit b2fbaf8

Browse files
committed
Refactor WeakListenerManager into ObjectManager and ListenerManager
1 parent 8ea378e commit b2fbaf8

File tree

11 files changed

+437
-213
lines changed

11 files changed

+437
-213
lines changed

.idea/compiler.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

baseLib/src/main/java/me/ycdev/android/lib/common/ipc/ServiceConnector.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import android.os.SystemClock
1414
import androidx.annotation.IntDef
1515
import androidx.annotation.VisibleForTesting
1616
import androidx.annotation.WorkerThread
17+
import me.ycdev.android.lib.common.manager.ListenerManager
1718
import me.ycdev.android.lib.common.utils.Preconditions
18-
import me.ycdev.android.lib.common.utils.WeakListenerManager
1919
import timber.log.Timber
2020
import java.util.concurrent.atomic.AtomicInteger
2121

@@ -32,7 +32,7 @@ abstract class ServiceConnector<IServiceInterface> protected constructor(
3232
) {
3333
protected var appContext: Context = cxt.applicationContext
3434

35-
protected var stateListeners = WeakListenerManager<ConnectStateListener>()
35+
protected var stateListeners = ListenerManager<ConnectStateListener>(true)
3636
private val connectWaitLock = Any()
3737
private val state = AtomicInteger(STATE_DISCONNECTED)
3838
private var connectStartTime: Long = 0
@@ -94,7 +94,7 @@ abstract class ServiceConnector<IServiceInterface> protected constructor(
9494
}
9595

9696
/**
97-
* Add a connect state listener, using [WeakListenerManager] to manager listeners.
97+
* Add a connect state listener, using [ListenerManager] to manager listeners.
9898
* Callbacks will be invoked in [.getConnectLooper] thread.
9999
*/
100100
fun addListener(listener: ConnectStateListener) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package me.ycdev.android.lib.common.manager
2+
3+
@Suppress("unused")
4+
open class ListenerManager<IListener : Any>(override val weakReference: Boolean) :
5+
ObjectManager<IListener>(weakReference) {
6+
7+
/**
8+
* Only invoked when invoke [addListener]
9+
*/
10+
protected open fun onFirstListenerAdd() {
11+
// nothing to do
12+
}
13+
14+
/**
15+
* Only invoked when invoke [removeListener]
16+
*/
17+
protected open fun onLastListenerRemoved() {
18+
// nothing to do
19+
}
20+
21+
/**
22+
* Override this method to notify the listener when registered.
23+
*/
24+
protected open fun onListenerAdded(listener: IListener) {
25+
// nothing to do
26+
}
27+
28+
final override fun onFirstObjectAdd() {
29+
onFirstListenerAdd()
30+
}
31+
32+
final override fun onLastObjectRemoved() {
33+
onLastListenerRemoved()
34+
}
35+
36+
final override fun onObjectAdded(obj: IListener) {
37+
onListenerAdded(obj)
38+
}
39+
40+
/**
41+
* Get the listeners count right now.
42+
* But the returned value may be NOT accurate if [weakReference] is true.
43+
* Some of the listeners may be already collected by GC.
44+
*/
45+
val listenersCount: Int by ::objectsCount
46+
47+
fun addListener(listener: IListener) = super.addObject(listener)
48+
49+
fun addListener(listener: IListener, tag: String) = super.addObject(listener, tag)
50+
51+
fun removeListener(listener: IListener) = super.removeObject(listener)
52+
53+
fun notifyListeners(action: NotifyAction<IListener>) = super.notifyObjects(action)
54+
55+
fun notifyListeners(action: (IListener) -> Unit) = super.notifyObjects(action)
56+
57+
companion object {
58+
private const val TAG = "ListenerManager"
59+
}
60+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package me.ycdev.android.lib.common.manager
2+
3+
interface NotifyAction<IListener> {
4+
fun notify(listener: IListener)
5+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package me.ycdev.android.lib.common.manager
2+
3+
import timber.log.Timber
4+
import java.lang.ref.WeakReference
5+
import java.util.ArrayList
6+
7+
open class ObjectManager<IObject : Any>(open val weakReference: Boolean) {
8+
private class ObjectInfo<IObject>(obj: IObject, val tag: String, weakReference: Boolean) {
9+
private var obj: IObject? = null
10+
private var holder: WeakReference<IObject>? = null
11+
12+
init {
13+
if (weakReference) {
14+
holder = WeakReference(obj)
15+
} else {
16+
this.obj = obj
17+
}
18+
}
19+
20+
fun getObject(): IObject? {
21+
return obj ?: holder!!.get()
22+
}
23+
}
24+
25+
private val allObjects: MutableList<ObjectInfo<IObject>> = ArrayList()
26+
27+
/**
28+
* Get the objects count right now.
29+
* But the returned value may be NOT accurate if [weakReference] is true.
30+
* Some of the objects may be already collected by GC.
31+
*/
32+
val objectsCount: Int get() = allObjects.size
33+
34+
/**
35+
* Only invoked when invoke [addObject]
36+
*/
37+
protected open fun onFirstObjectAdd() {
38+
// nothing to do
39+
}
40+
41+
/**
42+
* Only invoked when invoke [removeObject]
43+
*/
44+
protected open fun onLastObjectRemoved() {
45+
// nothing to do
46+
}
47+
48+
/**
49+
* Override this method to notify the listener when registered.
50+
*/
51+
protected open fun onObjectAdded(obj: IObject) {
52+
// nothing to do
53+
}
54+
55+
fun addObject(obj: IObject) {
56+
addObject(obj, obj::class.java.name)
57+
}
58+
59+
/**
60+
* @param tag Identity the object, for debug only
61+
*/
62+
fun addObject(obj: IObject, tag: String) {
63+
synchronized(allObjects) {
64+
if (allObjects.size == 0) {
65+
onFirstObjectAdd()
66+
}
67+
for (objectInfo in allObjects) {
68+
if (obj == objectInfo.getObject()) return // skip duplicate object
69+
}
70+
allObjects.add(ObjectInfo(obj, tag, weakReference))
71+
}
72+
73+
// Notify the listener to get initialized
74+
onObjectAdded(obj)
75+
}
76+
77+
fun removeObject(obj: IObject) {
78+
synchronized(allObjects) {
79+
var removed = false
80+
for (i in 0 until allObjects.size) {
81+
val objectInfo = allObjects[i]
82+
if (obj == objectInfo.getObject()) {
83+
allObjects.removeAt(i)
84+
removed = true
85+
break
86+
}
87+
}
88+
if (allObjects.size == 0 && removed) {
89+
onLastObjectRemoved()
90+
}
91+
}
92+
}
93+
94+
fun notifyObjects(action: NotifyAction<IObject>) {
95+
notifyObjects { action.notify(it) }
96+
}
97+
98+
fun notifyObjects(action: (IObject) -> Unit) {
99+
synchronized(allObjects) {
100+
// The object may remove itself!
101+
val objectsCopied: List<ObjectInfo<IObject>> = ArrayList(allObjects)
102+
for (i in objectsCopied.indices) {
103+
val objectInfo = objectsCopied[i]
104+
val obj: IObject? = objectInfo.getObject()
105+
if (obj == null) {
106+
Timber.tag(TAG).e("object leak found: %s", objectInfo.tag)
107+
allObjects.remove(objectInfo)
108+
} else {
109+
if (DEV_LOG) {
110+
Timber.tag(TAG).d("notify #%d: %s", i, objectInfo.tag)
111+
}
112+
action(obj)
113+
}
114+
}
115+
if (DEV_LOG) {
116+
Timber.tag(TAG).d("notify done, cur size: %d", allObjects.size)
117+
}
118+
}
119+
}
120+
121+
companion object {
122+
private const val TAG = "ObjectManager"
123+
private const val DEV_LOG = false
124+
}
125+
}

baseLib/src/main/java/me/ycdev/android/lib/common/tracker/WeakTracker.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package me.ycdev.android.lib.common.tracker
22

3-
import me.ycdev.android.lib.common.utils.WeakListenerManager
3+
import me.ycdev.android.lib.common.manager.ListenerManager
44

5-
abstract class WeakTracker<IListener : Any> : WeakListenerManager<IListener>() {
5+
abstract class WeakTracker<IListener : Any> : ListenerManager<IListener>(true) {
66
protected abstract fun startTracker()
77
protected abstract fun stopTracker()
88

baseLib/src/main/java/me/ycdev/android/lib/common/utils/WeakListenerManager.kt

Lines changed: 0 additions & 105 deletions
This file was deleted.

0 commit comments

Comments
 (0)