48274

Android - switching between Development and Production Web Services

Question:

I want to have my app switch between development and production web services without changing too much in the code (and be relatively fool proof).

Right now I have my web service addresses as static final String variables in the class that does the actual HTTP calls and switch code in the rest of the app by using a static final boolean.

Now whereas the boolean is pretty convenient, I can't use it to change the web service addresses because they are themselves static final variables. What is the best practice for tackling this ?

I found some discussions on SO that directed towards using Android SDK provided debug variable but although it can replace the boolean, it doesn't solve the other problem.

NOTE: In case you are wondering, I'm using the web services class statically so I don't have a constructor in which I can check the debug variable and change variables, plus I'd also like them to be static final.

Answer1:

I promised I'd do it and here it is:

<ul><li><a href="http://blog.blundellapps.com/switching-android-configurations-using-constants-and-ant/" rel="nofollow">http://blog.blundellapps.com/switching-android-configurations-using-constants-and-ant/</a></li> <li><a href="http://blog.blundellapps.com/running-an-ant-script-with-eclipse/" rel="nofollow">http://blog.blundellapps.com/running-an-ant-script-with-eclipse/</a></li> </ul>

Also mirrored on GitHub: <a href="https://github.com/blundell/BuildChoiceTut" rel="nofollow">https://github.com/blundell/BuildChoiceTut</a>

With the tutorial, you can switch configurations using Ant build scripts.

You setup a directory called /config/ in here you hold all your constants for different configurations. Once for each file i.e. live.props dev.props beta.props

Then, when Ant runs, it will read the selected file and 'inject' them into your build just before it is compiled.

Enjoy!

Answer2:

<strong>UPDATE</strong>

With the gradle build system this is now significantly easier. You can put a file, say Server.java with development server credentials in src/debug/java and the one with production credentials in src/release/java (assuming you are using the default gradle project configuration, adjust accordingly for custom). The build system then uses the appropriate file based on your buildType.

Even better, I now have the release file use static final variables whereas for the debug build I use static variables that are used in exactly the same way in code (e.g. Server.url, Server.username, etc.) but can be changed from a development drawer. What's the development drawer ? For an example of this see Jake Wharton's <a href="https://github.com/JakeWharton/u2020" rel="nofollow">u2020</a> project and associated talk.

<hr />

<strong>OLD ANSWER</strong>

I ended up using static methods to access the addresses with another static final boolean that defines the debug state. Like so :

public static String getWSAddress() { if(DEVELOPMENT_MODE) { return "http://dev.server.com"; } return "http://prod.server.com"; }

From what I've read, since the boolean is static final, the compiler will optimize and the condition code will be removed as unreachable.

This seems to be a suitable solution for this problem but the Android literature states that method calls are in general more expensive than direct variable access so can't decide whether this is a better solution than the one offered by Chuck.

<strong>Edit:</strong> For the record. I've moved to @Blundell's solution. Which is awesome. So I would recommend starting with this if you want it over quick but putting that on your roadmap.

Answer3:

If you really need the configuration to be in a static final constant, the solution is a litte more complex. This solution also still depends on debuggable being correctly set in your manifest, so it's not completely fool proof.

The only way I can think of to help you remember debuggable is on is to use the debug flag mentioned later to change the initial screen's background color to be red. A little silly, but would work.

You can declare the address variable as static final and not assign a value to it:

public static final String webServiceAddress;

You can get information on whether your app is set to de debuggable using:

getApplicationInfo().flags;

This means that you still need to flip a switch when you release an app and set debuggable to false in your manifest, but you should probably do this anyway to turn off log messages you don't want users to see.

In your default activity's onCreate, you can use this to branch and assign the correct address. Here's a complete example.

//Store release configuration here, using final constants public class ReleaseConfig { //Don't set webServiceAddress yet public static final String webSericeAddress; public static boolean configSet = false; static { //Set it as soon as this class is accessed //As long as this class is first accessed after the main activity's onCreate // runs, we can set this info in a final constant webServiceAddress = MainActivity.configuredAddress; } } public class MainActivity extends Activity { public static String configuredAddress; //This should be one of the first methods called in the activity public void onCreate(Bundle savedInstanceState) { //Figure out if we are in debug mode or not boolean debuggable = (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)); //Save off our debug configuration if (debuggable) configuredAddress = "the debuggable address"; else configuredAddress = "the production address"; //Access the static class, which will run it's static init block //By the time this runs, we'll have the info we need to set the final constant ReleaseConfig.configSet = true; }

Answer4:

You can use the <strong>Product Flavors</strong> for your app i.e. one flavor for dev and another for production. Give unique applicationId for each flavor.

Then in your code, check what product flavor you're using by checking the value of BuildConfig.APPLICATION_ID and use the url accordingly.

Hope this helps.

Recommend

  • How to read a file from line x to line y (with php)
  • reactjs - fetch as google displays blank page only
  • Memory size of a list of int
  • Table Valued Parameters with Estimated Number of Rows 1
  • iPhone 5's low light boost mode
  • Laravel 5 - Logout a user from all of his devices
  • Why Python does not see all the rows in a file?
  • Why does moving the buffer pointer slow down fread (C programming language)?
  • PHP set_time_limit no effect
  • C# split a list into all combinations of n groups - code migration from Python
  • Performance issues when using iterators?
  • Fastest array lookup algorithm in C for embedded system?
  • LDA: Why sampling for inference of a new document?
  • FIR filter in CUDA (as a 1D convolution)
  • Javascript syntax null : ?{}
  • Illegal reflective access operation
  • How to get to older Xcode beta version?
  • jQueryMobile, Ajax Navigation, and MVC
  • How can I reset dropdown data if modal closed on vue component?
  • Undefined navigator.push React-native 0.43.4
  • aapt.exe'' finished with non-zero exit value 1
  • Image map in Flex
  • Who propagate bugfixes across branches (corporate development)?
  • Android Studio 1.3 RC3. Google Play services out of date. Requires 7571000 but found 6774470
  • ViewController With Transparent Background Entering Current ViewController With Push Transition
  • MySQL Order by column = x, column asc?
  • Installing iPhone App to iPhone
  • Installing Hadoop, Java Exception about illegal characters at index 7?
  • C# - Is there a limit to the size of an httpWebRequest stream?
  • Is my CUDA kernel really runs on device or is being mistekenly executed by host in emulation?
  • Perl system calls when running as another user using sudo
  • Arrays break string types in Julia
  • Windows forms listbox.selecteditem displaying “System.Data.DataRowView” instead of actual value
  • InvalidAuthenticityToken between subdomains when logging in with Rails app
  • Proper folder structure for lots of source files
  • SQL merge duplicate rows and join values that are different
  • How does Linux kernel interrupt the application?
  • LevelDB C iterator
  • Can't mass-assign protected attributes when import data from csv file
  • How to push additional view controllers onto NavigationController but keep the TabBar?