简体   繁体   中英

Android - switching between Development and Production Web Services

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 .

UPDATE

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 (eg 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 u2020 project and associated talk.


OLD ANSWER

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.

Edit: 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.

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

Also mirrored on GitHub: https://github.com/blundell/BuildChoiceTut

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 ie 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!

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;
    }

You can use the Product Flavors for your app ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM