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