Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3488f1af authored by Hui Yu's avatar Hui Yu
Browse files

Use ArraySet for the list of observer WeakReference.

On a real device, the mObservers list size can be close to 20k.
Previously mObservers is an ArrayList of strong Reference and there
is memory leak from it. This is why we changed to WeakReference. It
takes 2ms to remove an Reference element from a 20k ArrayList. But It
takes 9ms to remove a WeakReference element from a 20k ArrayList
. This increases chance of ANR. Now we change from ArrayList to
ArraySet, removing a WeakReference element from a 20k ArraySet only
takes 0.1ms. The memory difference between ArrayList and ArraySet is
minimum.

Fix: 111555898
Test: compare time takes to remove WeakReference element from ArrayList
and ArraySet.

Change-Id: If77633549197bcb3cf5269b537288f0189f2fcf3
parent 81b6aee2
Loading
Loading
Loading
Loading
+50 −19
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
@@ -1062,7 +1063,34 @@ public class BatteryStatsImpl extends BatteryStats {
    // methods are protected not private to be VisibleForTesting
    public static class TimeBase {
        protected final ArrayList<WeakReference<TimeBaseObs>> mObservers = new ArrayList<>();
        private static class ObsWeakReference extends WeakReference<TimeBaseObs> {
            public ObsWeakReference(TimeBaseObs referent) {
                super(referent);
            }
            @Override
            public boolean equals(Object obj) {
                if (this == obj) {
                    return true;
                }
                if (!(obj instanceof ObsWeakReference)) {
                    return false;
                }
                return this.get() == ((ObsWeakReference)obj).get();
            }
            @Override
            public int hashCode() {
                TimeBaseObs obs = get();
                if (obs != null) {
                    return obs.hashCode();
                } else {
                    return 0;
                }
            }
        }
        protected final ArraySet<ObsWeakReference> mObservers = new ArraySet<>();
        protected long mUptime;
        protected long mRealtime;
@@ -1106,24 +1134,17 @@ public class BatteryStatsImpl extends BatteryStats {
        }
        public void add(TimeBaseObs observer) {
            mObservers.add(new WeakReference<TimeBaseObs>(observer));
            mObservers.add(new ObsWeakReference(observer));
        }
        public void remove(TimeBaseObs observer) {
           if (!mObservers.removeIf(ref -> ref.get() == observer)) {
            if (!mObservers.remove(new ObsWeakReference(observer))) {
                Slog.wtf(TAG, "Removed unknown observer: " + observer);
            }
        }
        public boolean hasObserver(TimeBaseObs observer) {
            Iterator<WeakReference<TimeBaseObs>> i = mObservers.iterator();
            while (i.hasNext()) {
                TimeBaseObs obs = i.next().get();
                if (obs == observer) {
                    return true;
                }
            }
            return false;
            return mObservers.contains(new ObsWeakReference(observer));
        }
        public void init(long uptime, long realtime) {
@@ -1205,6 +1226,8 @@ public class BatteryStatsImpl extends BatteryStats {
        }
        public boolean setRunning(boolean running, long uptime, long realtime) {
            int total = mObservers.size();
            int removed = 0;
            if (mRunning != running) {
                mRunning = running;
                if (running) {
@@ -1212,26 +1235,34 @@ public class BatteryStatsImpl extends BatteryStats {
                    mRealtimeStart = realtime;
                    long batteryUptime = mUnpluggedUptime = getUptime(uptime);
                    long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
                    for (WeakReference<TimeBaseObs> ref : mObservers) {
                        TimeBaseObs obs = ref.get();
                    for(int i = mObservers.size() - 1; i >= 0; i--) {
                        final TimeBaseObs obs = mObservers.valueAt(i).get();
                        if (obs != null) {
                            obs.onTimeStarted(realtime, batteryUptime, batteryRealtime);
                        } else {
                            mObservers.removeAt(i);
                            removed++;
                        }
                    }
                } else {
                    mPastUptime += uptime - mUptimeStart;
                    mPastRealtime += realtime - mRealtimeStart;
                    long batteryUptime = getUptime(uptime);
                    long batteryRealtime = getRealtime(realtime);
                    for (WeakReference<TimeBaseObs> ref : mObservers) {
                        TimeBaseObs obs = ref.get();
                    for(int i = mObservers.size() - 1; i >= 0; i--) {
                        final TimeBaseObs obs = mObservers.valueAt(i).get();
                        if (obs != null) {
                            obs.onTimeStopped(realtime, batteryUptime, batteryRealtime);
                        } else {
                            mObservers.removeAt(i);
                            removed++;
                        }
                    }
                }
                if (DEBUG && removed != 0) {
                    Slog.d(TAG,
                            "TimeBase observer removed:" + removed + " from total:" + total);
                }
                mObservers.removeIf(ref -> ref.get() == null);
                return true;
            }
            return false;