From 2a1427054d41aa4e7588c00c39fc47816c76fc45 Mon Sep 17 00:00:00 2001
From: str4d <str4d@mail.i2p>
Date: Wed, 16 Jul 2014 01:28:49 +0000
Subject: [PATCH] Use static int constants for State instead of Enums

Enums often require more than twice as much memory as static constants, and
should be strictly avoided on Android.
https://developer.android.com/training/articles/memory.html#Overhead

The advantage of this change is that client library users can directly compare
the status values they get from IRouterState to the constants, instead of
parsing a string representation of an Enum.
---
 .../net/i2p/android/router/MainActivity.java  | 24 +++++----
 .../net/i2p/android/router/MainFragment.java  | 52 +++++++++----------
 .../android/router/service/RouterService.java | 46 +++++-----------
 .../android/router/service/IRouterState.aidl  |  2 +-
 .../router/service/IRouterStateCallback.aidl  |  2 +-
 .../net/i2p/android/router/service/State.java | 30 +++++++++++
 6 files changed, 85 insertions(+), 71 deletions(-)
 create mode 100644 client/src/main/java/net/i2p/android/router/service/State.java

diff --git a/app/src/main/java/net/i2p/android/router/MainActivity.java b/app/src/main/java/net/i2p/android/router/MainActivity.java
index a167d1390..7cddfa874 100644
--- a/app/src/main/java/net/i2p/android/router/MainActivity.java
+++ b/app/src/main/java/net/i2p/android/router/MainActivity.java
@@ -21,12 +21,13 @@ import android.support.v4.app.DialogFragment;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-import net.i2p.android.router.R;
+
 import net.i2p.android.router.dialog.AboutDialog;
 import net.i2p.android.router.dialog.TextResourceDialog;
 import net.i2p.android.router.service.IRouterState;
 import net.i2p.android.router.service.IRouterStateCallback;
 import net.i2p.android.router.service.RouterService;
+import net.i2p.android.router.service.State;
 import net.i2p.android.router.util.Util;
 import net.i2p.router.RouterContext;
 import net.i2p.util.OrderedProperties;
@@ -154,9 +155,9 @@ public class MainActivity extends I2PActivityBase implements
                 if (mStateService.isStarted()) {
                     // Update for the current state.
                     Util.d("Fetching state.");
-                    String curState = mStateService.getState();
+                    int curState = mStateService.getState();
                     Message msg = mHandler.obtainMessage(STATE_MSG);
-                    msg.getData().putString("state", curState);
+                    msg.getData().putInt(MSG_DATA, curState);
                     mHandler.sendMessage(msg);
                 } else {
                     Util.d("StateService not started yet");
@@ -252,9 +253,9 @@ public class MainActivity extends I2PActivityBase implements
                     mStateService.registerCallback(mStateCallback);
                     // Update for the current state.
                     Util.d("Fetching state.");
-                    String curState = mStateService.getState();
+                    int curState = mStateService.getState();
                     Message msg = mHandler.obtainMessage(STATE_MSG);
-                    msg.getData().putString("state", curState);
+                    msg.getData().putInt(MSG_DATA, curState);
                     mHandler.sendMessage(msg);
                 } else {
                     // Unbind
@@ -285,23 +286,24 @@ public class MainActivity extends I2PActivityBase implements
          * NOT be running in our main thread like most other things -- so,
          * to update the UI, we need to use a Handler to hop over there.
          */
-        public void stateChanged(String newState) throws RemoteException {
+        public void stateChanged(int newState) throws RemoteException {
             Message msg = mHandler.obtainMessage(STATE_MSG);
-            msg.getData().putString("state", newState);
+            msg.getData().putInt(MSG_DATA, newState);
             mHandler.sendMessage(msg);
         }
     };
 
     private static final int STATE_MSG = 1;
+    private static final String MSG_DATA = "state";
 
     private Handler mHandler = new Handler() {
-        private String lastRouterState = null;
+        private int lastRouterState = -1;
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case STATE_MSG:
-                String state = msg.getData().getString("state");
-                if (lastRouterState == null || !lastRouterState.equals(state)) {
+                int state = msg.getData().getInt(MSG_DATA);
+                if (lastRouterState == -1 || lastRouterState != state) {
                     if (mMainFragment == null)
                         mMainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
                     if (mMainFragment != null) {
@@ -309,7 +311,7 @@ public class MainActivity extends I2PActivityBase implements
                         lastRouterState = state;
                     }
 
-                    if ("RUNNING".equals(state) && mAutoStartFromIntent) {
+                    if (state == State.RUNNING && mAutoStartFromIntent) {
                         setResult(RESULT_OK);
                         finish();
                     }
diff --git a/app/src/main/java/net/i2p/android/router/MainFragment.java b/app/src/main/java/net/i2p/android/router/MainFragment.java
index 47e9f9ab5..b96d9f133 100644
--- a/app/src/main/java/net/i2p/android/router/MainFragment.java
+++ b/app/src/main/java/net/i2p/android/router/MainFragment.java
@@ -8,24 +8,17 @@ import android.preference.PreferenceManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TableLayout;
 import android.widget.TableRow;
-import android.widget.ToggleButton;
-import android.widget.ImageView;
 import android.widget.TextView;
+import android.widget.ToggleButton;
 
-import java.text.Collator;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import net.i2p.android.router.R;
 import net.i2p.android.router.dialog.FirstStartDialog;
 import net.i2p.android.router.dialog.VersionDialog;
+import net.i2p.android.router.service.State;
 import net.i2p.android.router.util.LongToggleButton;
 import net.i2p.android.router.util.Util;
 import net.i2p.data.DataHelper;
@@ -36,6 +29,13 @@ import net.i2p.router.RouterContext;
 import net.i2p.router.TunnelPoolSettings;
 import net.i2p.util.Translate;
 
+import java.text.Collator;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
 public class MainFragment extends I2PFragmentBase {
 
     private Handler _handler;
@@ -198,7 +198,7 @@ public class MainFragment extends I2PFragmentBase {
             // is not received. Ensure that the state image is correct.
             // TODO: Fix the race between RouterService shutdown and
             // IRouterState unbinding.
-            updateState("INIT");
+            updateState(State.INIT);
         }
     }
 
@@ -235,25 +235,25 @@ public class MainFragment extends I2PFragmentBase {
         }
     }
 
-    public void updateState(String newState) {
+    public void updateState(int newState) {
         final ImageView lightImage = (ImageView) getView().findViewById(R.id.main_lights);
-        if ("INIT".equals(newState) ||
-                "STOPPED".equals(newState) ||
-                "MANUAL_STOPPED".equals(newState) ||
-                "MANUAL_QUITTED".equals(newState) ||
-                "NETWORK_STOPPED".equals(newState)) {
+        if (newState == State.INIT ||
+                newState == State.STOPPED ||
+                newState == State.MANUAL_STOPPED ||
+                newState == State.MANUAL_QUITTED ||
+                newState == State.NETWORK_STOPPED) {
             lightImage.setImageResource(R.drawable.routerlogo_0);
-        } else if ("STARTING".equals(newState) ||
-                "STOPPING".equals(newState) ||
-                "MANUAL_STOPPING".equals(newState) ||
-                "MANUAL_QUITTING".equals(newState) ||
-                "NETWORK_STOPPING".equals(newState)) {
+        } else if (newState == State.STARTING ||
+                newState == State.STOPPING ||
+                newState == State.MANUAL_STOPPING ||
+                newState == State.MANUAL_QUITTING ||
+                newState == State.NETWORK_STOPPING) {
             lightImage.setImageResource(R.drawable.routerlogo_1);
-        } else if ("RUNNING".equals(newState)) {
+        } else if (newState == State.RUNNING) {
             lightImage.setImageResource(R.drawable.routerlogo_2);
-        } else if ("ACTIVE".equals(newState)) {
+        } else if (newState == State.ACTIVE) {
             lightImage.setImageResource(R.drawable.routerlogo_3);
-        } else if ("WAITING".equals(newState)) {
+        } else if (newState == State.WAITING) {
             lightImage.setImageResource(R.drawable.routerlogo_4);
         } // Ignore unknown states.
     }
@@ -266,7 +266,7 @@ public class MainFragment extends I2PFragmentBase {
 
         if(!Util.isConnected(getActivity())) {
             // Manually set state, RouterService won't be running
-            updateState("WAITING");
+            updateState(State.WAITING);
             vStatusText.setText("No Internet connection is available");
             vStatus.setVisibility(View.VISIBLE);
             sv.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/net/i2p/android/router/service/RouterService.java b/app/src/main/java/net/i2p/android/router/service/RouterService.java
index 3034f4fd1..c9734786a 100644
--- a/app/src/main/java/net/i2p/android/router/service/RouterService.java
+++ b/app/src/main/java/net/i2p/android/router/service/RouterService.java
@@ -34,22 +34,10 @@ import net.i2p.util.OrderedProperties;
  */
 public class RouterService extends Service {
 
-    // These states persist even if we died... Yuck, it causes issues.
-    public enum State {
-        INIT, WAITING, STARTING, RUNNING, ACTIVE,
-        // unplanned (router stopped itself), next: killSelf()
-        STOPPING, STOPPED,
-        // button, don't kill service when stopped, stay in MANUAL_STOPPED
-        MANUAL_STOPPING, MANUAL_STOPPED,
-        // button, DO kill service when stopped, next: killSelf()
-        MANUAL_QUITTING, MANUAL_QUITTED,
-        // Stopped by listener (no network), next: WAITING (spin waiting for network)
-        NETWORK_STOPPING, NETWORK_STOPPED
-    }
     private RouterContext _context;
     private String _myDir;
     //private String _apkPath;
-    private State _state = State.INIT;
+    private int _state = State.INIT;
     private Thread _starterThread;
     private StatusBar _statusBar;
     private Notifications _notif;
@@ -75,7 +63,7 @@ public class RouterService extends Service {
     @Override
     public void onCreate() {
         mStartCalled = false;
-        State lastState = getSavedState();
+        int lastState = getSavedState();
         setState(State.INIT);
         Util.d(this + " onCreate called"
                 + " Saved state is: " + lastState
@@ -457,8 +445,8 @@ public class RouterService extends Service {
             return mStartCalled;
         }
 
-        public String getState() throws RemoteException {
-            return _state.name();
+        public int getState() throws RemoteException {
+            return _state;
         }
     };
 
@@ -493,8 +481,8 @@ public class RouterService extends Service {
     /**
      * debug
      */
-    public String getState() {
-        return _state.toString();
+    public int getState() {
+        return _state;
     }
 
     public boolean canManualStop() {
@@ -596,7 +584,7 @@ public class RouterService extends Service {
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case STATE_MSG:
-                String state = _state.name();
+                final int state = _state;
                 // Broadcast to all clients the new state.
                 final int N = mStateCallbacks.beginBroadcast();
                 for (int i = 0; i < N; i++) {
@@ -658,13 +646,13 @@ public class RouterService extends Service {
      */
     private class Stopper implements Runnable {
 
-        private final State nextState;
-        private final State stopState;
+        private final int nextState;
+        private final int stopState;
 
         /**
          * call holding statelock
          */
-        public Stopper(State next, State stop) {
+        public Stopper(int next, int stop) {
             nextState = next;
             stopState = stop;
             setState(next);
@@ -776,18 +764,12 @@ public class RouterService extends Service {
         }
     }
 
-    private State getSavedState() {
+    private int getSavedState() {
         SharedPreferences prefs = getSharedPreferences(SHARED_PREFS, 0);
-        String stateString = prefs.getString(LAST_STATE, State.INIT.toString());
-        for(State s : State.values()) {
-            if(s.toString().equals(stateString)) {
-                return s;
-            }
-        }
-        return State.INIT;
+        return prefs.getInt(LAST_STATE, State.INIT);
     }
 
-    private void setState(State s) {
+    private void setState(int s) {
         _state = s;
         saveState();
         mHandler.sendEmptyMessage(STATE_MSG);
@@ -799,7 +781,7 @@ public class RouterService extends Service {
     private boolean saveState() {
         SharedPreferences prefs = getSharedPreferences(SHARED_PREFS, 0);
         SharedPreferences.Editor edit = prefs.edit();
-        edit.putString(LAST_STATE, _state.toString());
+        edit.putInt(LAST_STATE, _state);
         return edit.commit();
     }
 }
diff --git a/client/src/main/aidl/net/i2p/android/router/service/IRouterState.aidl b/client/src/main/aidl/net/i2p/android/router/service/IRouterState.aidl
index 1a087bc4a..95cf3d636 100644
--- a/client/src/main/aidl/net/i2p/android/router/service/IRouterState.aidl
+++ b/client/src/main/aidl/net/i2p/android/router/service/IRouterState.aidl
@@ -28,6 +28,6 @@ interface IRouterState {
     /**
     * Get the state of the I2P router
     **/
-    String getState();
+    int getState();
 
 }
diff --git a/client/src/main/aidl/net/i2p/android/router/service/IRouterStateCallback.aidl b/client/src/main/aidl/net/i2p/android/router/service/IRouterStateCallback.aidl
index a5ee84c1d..20221b4c3 100644
--- a/client/src/main/aidl/net/i2p/android/router/service/IRouterStateCallback.aidl
+++ b/client/src/main/aidl/net/i2p/android/router/service/IRouterStateCallback.aidl
@@ -9,5 +9,5 @@ oneway interface IRouterStateCallback {
     /**
      * Called when the state of the I2P router changes
      */
-    void stateChanged(String newState);
+    void stateChanged(int newState);
 }
diff --git a/client/src/main/java/net/i2p/android/router/service/State.java b/client/src/main/java/net/i2p/android/router/service/State.java
new file mode 100644
index 000000000..597564358
--- /dev/null
+++ b/client/src/main/java/net/i2p/android/router/service/State.java
@@ -0,0 +1,30 @@
+package net.i2p.android.router.service;
+
+/**
+ * Extracted from RouterService because Enums should be avoided on Android.
+ * <p/>
+ * https://developer.android.com/training/articles/memory.html#Overhead
+ *
+ * @author str4d
+ * @since 0.9.14
+ */
+public class State {
+    // These states persist even if we died... Yuck, it causes issues.
+    public static final int INIT = 0;
+    public static final int WAITING = 1;
+    public static final int STARTING = 2;
+    public static final int RUNNING = 3;
+    public static final int ACTIVE = 4;
+    // unplanned (router stopped itself), next: killSelf()
+    public static final int STOPPING = 5;
+    public static final int STOPPED = 6;
+    // button, don't kill service when stopped, stay in MANUAL_STOPPED
+    public static final int MANUAL_STOPPING = 7;
+    public static final int MANUAL_STOPPED = 8;
+    // button, DO kill service when stopped, next: killSelf()
+    public static final int MANUAL_QUITTING = 9;
+    public static final int MANUAL_QUITTED = 10;
+    // Stopped by listener (no network), next: WAITING (spin waiting for network)
+    public static final int NETWORK_STOPPING = 11;
+    public static final int NETWORK_STOPPED = 12;
+}
-- 
GitLab