Android: automatically choose debug/release Maps api key?

二次信任 提交于 2019-11-26 17:12:29

There is a new way to determine is it a debug build or release one in SDK Tools, Revision 17. An excerpt from new features overview:

Builds now generate a class called BuildConfig containing a DEBUG constant that is automatically set according to your build type. You can check the (BuildConfig.DEBUG) constant in your code to run debug-only functions.

So now you can simply write something like this:

if (BuildConfig.DEBUG)
{
    //Your debug code goes here
}
else
{
    //Your release code goes here
}

UPDATE: I've encountered bug in ADT: sometimes BuildConfig.DEBUG is true after exporting application package. Description is here: http://code.google.com/p/android/issues/detail?id=27940

Had the same hassle with the API key. Here's a full solution, based on the above link and example from Bijarni (which somehow didn't work for me), I use now this method:

// Define the debug signature hash (Android default debug cert). Code from sigs[i].hashCode()
protected final static int DEBUG_SIGNATURE_HASH = <your hash value>;

// Checks if this apk was built using the debug certificate
// Used e.g. for Google Maps API key determination (from: http://whereblogger.klaki.net/2009/10/choosing-android-maps-api-key-at-run.html)
public static Boolean isDebugBuild(Context context) {
    if (_isDebugBuild == null) {
        try {
            _isDebugBuild = false;
            Signature [] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
            for (int i = 0; i < sigs.length; i++) {
                if (sigs[i].hashCode() == DEBUG_SIGNATURE_HASH) {
                    Log.d(TAG, "This is a debug build!");
                    _isDebugBuild = true;
                    break;
                }
            }
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }      
    }
    return _isDebugBuild;
}

You have to find out your debug signature's hashValue() once, just output sigs[i].hashCode().

Then, I didn't want to dynamically add the MapView, but rather use the xml file. You cannot set the api key attribute in the code and use an xml layout, so I use this simple method (though copying the xml layout isn't so beautiful):

In my MapActivity:

    public void onCreate(Bundle savedInstanceState)
    {       
    super.onCreate(savedInstanceState);

    // Select the proper xml layout file which includes the matching Google API Key
    if (isDebugBuild(this)) {
        setContentView(R.layout.map_activity_debug);
    } else {
        setContentView(R.layout.map_activity_release);
    }

Much easier way to determine whether it is a debug build is by checking the debug flag on the application info than the signature hash.

public boolean isDebugBuild() throws Exception
{
   PackageManager pm = _context.getPackageManager();
   PackageInfo pi = pm.getPackageInfo(_context.getPackageName(), 0);

   return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
}

Once debug build is found, either you can use different resource for showing map or create the mapview within the app and add to a layout.

    if(isDebugBuild())
    {
        _mapView = new MapView(this, getString(R.string.debugmapskey));
    }
    else
    {
        _mapView = new MapView(this, getString(R.string.releasemapskey));
    }

I have worked around the horrendous mis-integration of the api keys into the build process and source control by making it a property stored in local.properties. I had to add the following to build.xml:

<property name="mapviewxml" value="res/layout/mapview.xml" />
<target name="-pre-build">
    <fail unless="mapsApiKey">You need to add mapsApiKey=... to local.properties</fail>
    <copy file="mapview.xml.tpl" tofile="${mapviewxml}" overwrite="true">
        <filterchain>
            <replacetokens>
                <token key="apiKey" value="${mapsApiKey}"/>
            </replacetokens>
        </filterchain>

    </copy>
</target>

Now, of course I had to create mapview.xml.tpl in my projects root (it can't go to res/layout because it will break the build process):

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:apiKey="@apiKey@"
    />

During pre-compilation, the template is copied to the right place and @apiKey@ is replaced with the real key. Unfortunately I have not found a way to distinguish between debug and release builds in this phase, so to compile for release, I just add the release apiKey to the ant parameters:

ant -DmapsApiKey=.... release 

This approach integrates well with SCM (I do not need to check in the keys) and acceptably with the build process.

If you're still interested I just blogged about another way to do this. With a simple change to the Android build script, you can switch the Map API key as well as all other required release changes. What I like about this is that nothing debug-related goes into the release, and you can keep the XML layouts just the way they were before.

http://blog.cuttleworks.com/2011/02/android-dev-prod-builds/

guibar

I think that creating an entry in the Google API's console which includes both your release key and your debug key (both mapping to the same package) works great and is a much simpler way to not have to worry about whether you are debuging or compiling a release version. The solution is outlined here

penduDev

All answers here seem outdated, if you are using android studio then gradle is the way to go

Use different keys in your build.gradle

android {
  .. .. ...
    buildTypes {
       debug {
          resValue "string", "google_maps_api_key", "[YOUR DEV KEY]"
       }
       release {
           resValue "string", "google_maps_api_key", "[YOUR PROD KEY]"
       }
    }
  }

And in your AndroidManifest.xml

<meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value="@string/google_maps_api_key"/>

source

And if you want to save some passwords for debug and release differently then you should follow this

I've ended up with the special file on SD card - if present, use debug key; missing - use release one. And it works.

EDIT: see new accepted answer, it works better

I don't know if this helps anyone but I have merged some of the other suggestions here to produce the following MapViewActivity.

In this example R.layout.map_dbg is only used if this is a debug build and the file exists (add this file to your .gitignore).

The advantages of this approach are :

  1. you don't need to write an ant target (good if you use eclipse)
  2. the correct release key is always in map.xml (hopefully a debug key won't be checked in by mistake)
  3. the release key is always used for a release build
  4. multiple debug keys can be used

The disadvantages of this approach are :

  1. you need to remember to update map_dbg.xml every time map.xml is updated

    public class MapViewActivity extends MapActivity {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //
            // copy the map.xml to map_dbg.xml and update the api key. 
            //
            int id = getLayoutId("map_dbg");
            if(id ==0)
                id = R.layout.map;
    
            setContentView(id);
        }
    
        int getLayoutId(String name) {
            return isDebugBuild() ? getResources().getIdentifier(name, "layout", getPackageName()) : 0;
        }
    
        public boolean isDebugBuild() 
        {
            boolean dbg = false;
            try {
                PackageManager pm = getPackageManager();
                PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
    
                dbg = ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
            } catch (Exception e) {
            }
            return dbg;
        }
    
    }
    

I have setup a simple ant target that replaces the apikey with either a debug key or a release key. This is really simple and keeps the code free of unwanted logic.

<target name="apikey">
    <!-- Location of target layout file -->
    <first id="first">
        <fileset dir="." includes="res/layout/kondi_training_templates.xml" />
    </first>
    <property name="layout-file" value="${toString:first}"/>
    <echo>template-file: ${template-file}</echo>

    <replaceregexp file="${template-file}"
        match="android:apiKey=.*"
        replace='android:apiKey="${mapview.apikey}"'
        byline="true"
    />
</target>
Zumry Mohamed

In Map V2 Its easy to send seperate keys using Android Studio Gradle tool. I have implemented an easy way for that. please check the link here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!