Skip to content

Commit acd72e9

Browse files
committed
change : optimize memory usage on iterators
1 parent 9452ed0 commit acd72e9

9 files changed

Lines changed: 173 additions & 65 deletions

File tree

floatstat/src/androidTest/java/id/psw/floatstat/ExampleInstrumentedTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class ExampleInstrumentedTest {
3434
@Test
3535
fun readTemperature(){
3636
val thermalDir = File("/sys/class/thermal")
37-
thermalDir.list { dir, _ -> dir.isDirectory }?.forEach {
37+
val lst = thermalDir.list { dir, _ -> dir.isDirectory }
38+
if(lst == null) return
39+
for(it in lst) {
3840
try{
3941
val ls = File(thermalDir, it)
4042
val typeFile = File(ls, "type")

floatstat/src/main/java/id/psw/floatstat/App.kt

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class App : Application() {
9696
val dataList : ArrayList<PluginDataInfo> = arrayListOf()){
9797

9898
fun updateData(ctx:App){
99-
dataList.forEach {
99+
dataList.forEachLA {
100100
val eligibleToUpdate = ctx.defaultPlugin.equals(this.name.className, it.id)
101101
|| ctx.activePlugins.indexOfFirst { pId -> pId.equals(this.name.className, it.id) } >= 0
102102
if(eligibleToUpdate) {
@@ -148,7 +148,8 @@ class App : Application() {
148148
val pluginDspName = displayNames[name] ?: name.toString()
149149
Log.d(TAG, "$name -> $pluginDspName")
150150
val vInfo = PluginInfo(asPlugin, name, pluginDspName)
151-
asPlugin.dataIds.split(',').forEach {
151+
val dp = asPlugin.dataIds.split(',')
152+
dp.forEachLA {
152153
val dName = asPlugin.getDataName(it)
153154
val dInfo = PluginDataInfo(
154155
it, dName,
@@ -173,7 +174,7 @@ class App : Application() {
173174
val i = Intent(ACTION_START_PLUGIN).addCategory(CATEGORY_PLUGIN)
174175
val dPkg = if (Build.VERSION.SDK_INT >= 33) {
175176
packageManager.queryIntentServices(i, PackageManager.ResolveInfoFlags.of(0L))
176-
} else @Suppress("DEPRECATION") { // Shut up! this is for older device the API did not even care anymore!
177+
} else { // Shut up! this is for older device the API did not even care anymore!
177178
packageManager.queryIntentServices(i, 0)
178179
}
179180
if(pluginConnector.hasBound){
@@ -182,7 +183,7 @@ class App : Application() {
182183
}
183184
pluginList.clear()
184185

185-
dPkg.forEach {
186+
dPkg.forEachLA {
186187
try{
187188
val cName = ComponentName(it.serviceInfo.packageName, it.serviceInfo.name)
188189
val sbi = Intent(ACTION_START_PLUGIN)
@@ -230,37 +231,42 @@ class App : Application() {
230231
if(!pref.contains(PK_ENABLED_PLUGIN)){
231232
val internalName = InternalStatProviderService::class.java.name
232233
val sb = StringBuilder()
233-
arrayListOf(
234+
val arr = arrayListOf(
234235
PluginId(internalName,InternalStatProviderService.DAT_TEMP_CPU),
235236
PluginId(internalName,InternalStatProviderService.DAT_TEMP_BATTERY),
236237
PluginId(internalName,InternalStatProviderService.DAT_RAM_USAGE),
237238
PluginId(internalName,InternalStatProviderService.DAT_BATTERY_INFO),
238239
PluginId(internalName,InternalStatProviderService.DAT_NET_UPLOAD),
239-
PluginId(internalName,InternalStatProviderService.DAT_NET_DNLOAD)
240-
).forEach {
240+
PluginId(internalName,InternalStatProviderService.DAT_NET_DNLOAD))
241+
242+
arr.forEachLA {
241243
sb.append(it.toString()).append(',')
242244
}
243245
// We need the next command to run after the write finished
244246
pref.edit().putString(PK_ENABLED_PLUGIN, sb.toString()).commit()
245247
}
246248
val plugins = pref.getString(PK_ENABLED_PLUGIN,"") ?: ""
247249
activePlugins.clear()
248-
plugins.split(",").forEach {
249-
if(it.isNotEmpty()){
250-
activePlugins.add(PluginId(it))
251-
}
250+
plugins.split(",").forEachLA {
251+
if(it.isEmpty()) return@forEachLA
252+
activePlugins.add(PluginId(it))
252253
}
253254
val defPlug = pref.getString(PK_DEFAULT_PLUGIN_DISPLAY, null)
254255
if(defPlug != null){
255256
defaultPlugin = PluginId(defPlug)
256257
}
257258
startOnBoot = pref.getBoolean(PK_RUN_ON_STARTUP, false)
258259
hideOnTap = pref.getBoolean(PK_HIDE_ON_TAP, false)
260+
261+
// Do GC Periodically
262+
System.gc()
259263
}
260264

261265
fun savePreferences(){
262266
val activePluginIds = StringBuilder()
263-
activePlugins.forEach { activePluginIds.append(it.toString()).append(',') }
267+
activePlugins.forEachLA {
268+
activePluginIds.append(it.toString()).append(',')
269+
}
264270
pref.edit()
265271
.putLong(PK_THREADED_UPDATE_FREQ, updateFrequency)
266272
.putString(PK_ENABLED_PLUGIN, activePluginIds.toString())
@@ -276,9 +282,7 @@ class App : Application() {
276282
readPreferences()
277283
listPlugins()
278284
fixedRateTimer("pluginPoll", false, 1000L, 300L){
279-
pluginList.forEach { info ->
280-
info.updateData(ctx)
281-
}
285+
pluginList.forEachLA { info -> info.updateData(ctx) }
282286
}
283287

284288
fixedRateTimer("libPrefUpdater", false, 0L, 1000L ){
@@ -291,9 +295,7 @@ class App : Application() {
291295
private var onMemoryCleaning = ArrayList<(Int) -> Unit>()
292296

293297
override fun onTrimMemory(level: Int) {
294-
onMemoryCleaning.forEach {
295-
it.invoke(level)
296-
}
298+
onMemoryCleaning.forEachLA { it.invoke(level) }
297299
super.onTrimMemory(level)
298300
}
299301

@@ -318,9 +320,8 @@ class App : Application() {
318320
fun clearPlugins() {
319321
iconProvider.clearMemFile()
320322
unbindService(pluginConnector)
321-
pluginList.forEach {
322-
it.binder.requestStop()
323-
}
323+
324+
pluginList.forEachLA { it.binder.requestStop() }
324325
}
325326

326327
}

floatstat/src/main/java/id/psw/floatstat/Ext.kt

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package id.psw.floatstat
22

33
import android.content.Context
44
import android.os.Build
5+
import kotlin.math.max
56

67
fun sdkAtLeast(ver: Int) : Boolean = Build.VERSION.SDK_INT >= ver
78

@@ -10,4 +11,81 @@ val Context.app : App get(){
1011
else this.applicationContext as App
1112
}
1213

13-
fun <T> Boolean.select(a:T, b:T) : T = if(this) a else b
14+
fun <T> Boolean.select(a:T, b:T) : T = if(this) a else b
15+
16+
inline fun <T> Collection<T>.forEachLA(block: (T) -> Unit){
17+
val kSize = size
18+
var i =0
19+
while(i < kSize)
20+
{
21+
block(elementAt(i))
22+
i++
23+
}
24+
}
25+
26+
inline fun <T> Collection<T>.forEachIndexedLA(block: (Int, T) -> Unit){
27+
val kSize = size
28+
var i = 0
29+
while(i < kSize)
30+
{
31+
block(i, elementAt(i))
32+
i++
33+
}
34+
}
35+
36+
inline fun <T> Collection<T>.firstOrNullLA(block: (T) -> Boolean) : T?
37+
{
38+
val kSize = size
39+
var i = 0
40+
while(i < kSize) {
41+
if (block(elementAt(i))){
42+
return elementAt(i)
43+
}
44+
i++
45+
}
46+
return null
47+
}
48+
49+
inline fun <T> Array<T>.forEachLA(block: (T) -> Unit){
50+
val kSize = size
51+
var i =0
52+
while(i < kSize)
53+
{
54+
block(elementAt(i))
55+
i++
56+
}
57+
}
58+
59+
inline fun <T> Array<T>.forEachIndexedLA(block: (Int, T) -> Unit){
60+
val kSize = size
61+
var i = 0
62+
while(i < kSize)
63+
{
64+
block(i, elementAt(i))
65+
i++
66+
}
67+
}
68+
69+
inline fun <T> Array<T>.firstOrNullLA(block: (T) -> Boolean) : T?
70+
{
71+
val kSize = size
72+
var i = 0
73+
while(i < kSize) {
74+
if (block(elementAt(i))){
75+
return elementAt(i)
76+
}
77+
i++
78+
}
79+
return null
80+
}
81+
82+
inline fun <K, V> Map<K,V>.forEachLA (block : (K,V) -> Unit) {
83+
val kSize = max(keys.size, values.size)
84+
var i = 0
85+
while(i < kSize) {
86+
val k = keys.elementAt(i)
87+
val v = values.elementAt(i)
88+
block(k,v)
89+
i++
90+
}
91+
}

floatstat/src/main/java/id/psw/floatstat/FloatWindowService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ class FloatWindowService : Service() {
236236
selectorRView = RecyclerView(applicationContext)
237237
val dataList = arrayListOf<PluginSelectorItem>()
238238

239-
app.pluginList.forEach { pg ->
240-
pg.dataList.forEach { dt ->
239+
app.pluginList.forEachLA { pg ->
240+
pg.dataList.forEachLA { dt ->
241241
val pDspName = pg.displayName
242242
val dDspName = dt.displayName
243243
val enabled = app.activePlugins.firstOrNull { aPlug -> aPlug.equals(pg.name.className, dt.id) } != null
@@ -326,7 +326,7 @@ class FloatWindowService : Service() {
326326

327327
private fun saveOrderAndActivation(dataList: ArrayList<PluginSelectorItem>, selectedDefault: Int) {
328328
app.activePlugins.clear()
329-
dataList.filter { it.isActive }.forEach {
329+
dataList.filter { it.isActive }.forEachLA {
330330
app.activePlugins.add(App.PluginId(it.pkgName, it.id))
331331
}
332332
if(selectedDefault < dataList.size && selectedDefault >= 0){

floatstat/src/main/java/id/psw/floatstat/InternalStatProviderService.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ class InternalStatProviderService : Service() {
5858
.toBitmap(iconSize * 4, iconSize * 2, Bitmap.Config.ALPHA_8)
5959
val uvRect = Rect()
6060
val vpRect = Rect()
61-
arrayOf(
61+
val kArr = arrayOf(
6262
arrayOf(IC_BATTERY_LEVEL, IC_BATTERY_CHARGE, IC_BATTERY_LOW, IC_RAM_USAGE),
6363
arrayOf(IC_DOWNLINK, IC_UPLINK, IC_CPU_TEMP, IC_BAT_TEMP)
64-
).forEachIndexed { yIdx, yArr ->
65-
yArr.forEachIndexed { xIdx, id ->
64+
)
65+
66+
kArr.forEachIndexedLA { yIdx, yArr ->
67+
yArr.forEachIndexedLA { xIdx, id ->
6668
val x = xIdx * iconSize
6769
val y = yIdx * iconSize
6870
val ctBmp = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888)

floatstat/src/main/java/id/psw/floatstat/StatusView.kt

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,14 @@ class StatusView @JvmOverloads constructor(
7777

7878
private fun getMaxPluginDataValueWidth(){
7979
var maxWidth = 20.0f
80-
app.pluginList.forEach { plug ->
81-
plug.dataList.forEach {
80+
81+
app.pluginList.forEachLA{ plug ->
82+
plug.dataList.forEachLA {
8283
val w = textPaint.measureText(it.value)
8384
if(maxWidth < w) maxWidth = w
8485
}
8586
}
87+
8688
this.maxWidth = maxWidth + textPaint.textSize
8789
}
8890

@@ -124,8 +126,8 @@ class StatusView @JvmOverloads constructor(
124126
ctx.drawCircle(15.0f, 15.0f, 15.0f, drawPaint)
125127
textPaint.textAlign = Paint.Align.CENTER
126128
var text = ""
127-
app.pluginList.forEach { plug ->
128-
plug.dataList.forEach { dat ->
129+
app.pluginList.forEachLA { plug ->
130+
plug.dataList.forEachLA { dat ->
129131
if(app.defaultPlugin.equals(plug.name.className, dat.id)){
130132
text = dat.value
131133
textPaint.color = dat.textTint
@@ -137,6 +139,18 @@ class StatusView @JvmOverloads constructor(
137139
textPaint.color = Color.WHITE
138140
}
139141

142+
143+
private val duffFilters = mutableMapOf<Int, PorterDuffColorFilter>()
144+
private fun porterDuffFilterCache(color:Int) : PorterDuffColorFilter {
145+
var d = duffFilters[color]
146+
if(d == null) {
147+
d = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
148+
duffFilters[color] = d
149+
}
150+
151+
return d
152+
}
153+
140154
private fun onDrawExpanded(ctx:Canvas) {
141155
drawPaint.colorFilter = null
142156
drawPaint.color = Color.argb(128, 0, 0, 0)
@@ -147,31 +161,32 @@ class StatusView @JvmOverloads constructor(
147161
val icLeft = PADDING * 1.0f
148162
val txLeft = (PADDING * 2.0f) + lineSz
149163
val app = context.app
150-
app.activePlugins.forEachIndexed { i, aPlug ->
151-
app.pluginList.firstOrNull {
152-
plug -> plug.name.className == aPlug.pkg
153-
}?.also {
154-
plug -> plug.dataList.firstOrNull { dat -> dat.id == aPlug.id }
155-
?.also {
156-
val icTop = PADDING + (i * lineSz)
157-
val icon = it.icon
158-
if(icon != null && !icon.isRecycled){
159-
drawPaint.color = it.iconTint
160-
if(it.iconTint == Color.TRANSPARENT){
161-
drawPaint.colorFilter = null
162-
}else{
163-
drawPaint.colorFilter = PorterDuffColorFilter(it.iconTint, PorterDuff.Mode.SRC_IN)
164-
}
165-
drawRectF.set(
166-
icLeft, icTop,
167-
icLeft + lineSz, icTop + lineSz
168-
)
169-
ctx.drawBitmap(icon, null, drawRectF, drawPaint)
170-
}
171-
textPaint.color = it.textTint
172-
ctx.drawText(it.value, txLeft, icTop + textPaint.textSize, textPaint)
164+
app.activePlugins.forEachIndexedLA { i, aPlug ->
165+
val plugs = app.pluginList.firstOrNullLA { plug -> plug.name.className == aPlug.pkg }
166+
?: return@forEachIndexedLA
167+
168+
val dat = plugs.dataList.firstOrNullLA { dat -> dat.id == aPlug.id }
169+
?: return@forEachIndexedLA
170+
171+
val icTop = PADDING + (i * lineSz)
172+
val icon = dat.icon
173+
if(icon != null && !icon.isRecycled){
174+
drawPaint.color = dat.iconTint
175+
176+
if(dat.iconTint == Color.TRANSPARENT){
177+
drawPaint.colorFilter = null
178+
}else{
179+
drawPaint.colorFilter = porterDuffFilterCache(dat.iconTint)
173180
}
181+
182+
drawRectF.set(
183+
icLeft, icTop,
184+
icLeft + lineSz, icTop + lineSz
185+
)
186+
ctx.drawBitmap(icon, null, drawRectF, drawPaint)
174187
}
188+
textPaint.color = dat.textTint
189+
ctx.drawText(dat.value, txLeft, icTop + textPaint.textSize, textPaint)
175190
}
176191
}
177192

0 commit comments

Comments
 (0)