Google Maps Android API v2 throws GooglePlayServicesNotAvailableException, out of date, SupportMapFragment.getMap() returns null
K

3

18

I am trying to incorporate the new Google Maps Android API v2 into an application using the Android Support Library. I've gone through many iteration to try to get somewhere to work and now I have given up and thought I should ask here.

I went to the API Console, created a project, enabled Google Maps API v2 for Android, created a debug KEY as required and pasted it on the manifest. No authentication problems or anything like that. Pretty sure I did this right.

In my project's libs folder I put android-support-v4.jar (via project right-click->Android->Add Support Library...). In Eclipse I did File->Import->Android->Existing Android Code Into Workspace and imported the latest (rev 3) of android-sdk/google/google_play_services/libproject/google-play-services_lib. Then I added it as a dependency library to my project (project right-click->Properties->Android->Add it as a library dependency on the bottom.

In my manifest I have:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
    <permission
      android:name="myapp.permission.MAPS_RECEIVE"
      android:protectionLevel="signature"/>

    <All the uses-permission required like INTERNET, ACCESS_NETWORK_STATE, WRITE_EXTERNAL_STORAGE, FINE AND COARSE LOCATION, etc>
    <uses-permission android:name="myapp.permission.MAPS_RECEIVE"/>
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <application ...>
        ...
        <uses-library android:name="com.google.android.maps" />
        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AI..."/>
    </application>

In my mainview.xml I have:

<fragment
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/map"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   class="com.google.android.gms.maps.SupportMapFragment" />

Now on MainView.java I have:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;

public class MainView extends FragmentActivity ... {
    private GoogleMap mMap;

    @Override
    protected void onResume() {
        ...
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.map);
        SupportMapFragment mapFragment = (SupportMapFragment)fragment;
        mMap = mapFragment.getMap();
        //PROBLEM: mMap is null here even though mapFragment is not
    }
}

Then I thought I probably need to initialize the fragments so I added in my onCreate after setting the content view:

//Fragments
FragmentManager manager = getSupportFragmentManager(); 
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.map, SupportMapFragment.newInstance());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.commit();

Then I said, maybe it's not SupportMapFragment but I actually should refer to the actual fragment, and in my onResume I did:

Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.map);
SupportMapFragment mapFragment = (SupportMapFragment)fragment;

//Fragments
FragmentManager manager = getSupportFragmentManager(); 
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.map, mapFragment);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.commit();

mMap = mapFragment.getMap();

mMap is again null after this.

Then I saw there is a MapsInitializer class, so I thought maybe I should call that first.

So I added the code before getting the fragment in the above code:

try {
    MapsInitializer.initialize(this);
} catch (GooglePlayServicesNotAvailableException e) {
    e.printStackTrace();
}

With this Logcat reported the warning (line 313 is the MapsInitializer.initialize(this)):

com.google.android.gms.common.GooglePlayServicesNotAvailableException
    at com.google.android.gms.internal.n.c(Unknown Source)
    at com.google.android.gms.internal.n.a(Unknown Source)
    at com.google.android.gms.maps.MapsInitializer.initialize(Unknown Source)
    at myapp.activities.MainView.inflateSelectedViewAndSetData(MainView.java:313)
    at myapp.activities.MainView.onClick(MainView.java:642)
    at android.view.View.performClick(View.java:3153)
    at android.view.View$PerformClick.run(View.java:12148)
    at android.os.Handler.handleCallback(Handler.java:587)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:152)
    at android.app.ActivityThread.main(ActivityThread.java:4615)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
    at dalvik.system.NativeStart.main(Native Method)

Throughout my attempts Logcat would report the following warning:

Google Play services out of date.  Requires 2010100 but found 1013

This could be a/the culprit but I found no solution.

At this point I am out of ideas. I read several threads here, here, here but none of the suggestions resolved the problem. On the latter they suggest not including the project dependency and just including the jar file. Well, that did not work either using either google-play-services.jar from the google-play-services_lib/libs folder or exporting my own jar from that project.

By the way I am running on an actual device (2.3.5 and a tablet with 3.2), not the emulator.

Any help wholeheartedly appreciated. :)

UPDATE:

The solution is below by qubit.

As far exporting the library dependency, a neat way to not have to deal with it (which is a pain when dealing with repositories) is to use FatJar and export the google_play_services_lib project (which I DID NOT create a local copy from because when it updates I don't want to re-import), and include tha output fat jar file in your project as another jar file. NOTE: When choosing which jar files to include in the fat jar, I had to exclude the annotations.jar file as it was already pre-included and was causing errors for duplication. Now all I have to do is include the google_play_services_rev_3.jar in my project's libs folder and I am good to go.

Koeninger answered 5/12, 2012 at 11:24 Comment(3)
Since this isn't an answer to your problem I'll give this as comment only: when you don't want to do more than GPS/WLAN based localisation, showing of maps, drawing into shown maps I'd recommend to use the classes ouf of sourceforge.net/projects/libwlocate . IMHO they are much easier to handle and provide support for several map types.Minny
This "google_play_services_rev_3.jar" should be generic, same works for any project and configuration, I suppose? Thanks for the hint of FatJar, I got now my project working in Jenkins CI with this procedure.Forsythe
@Forsythe Yes, it's generic and you can import it on any project. :)Koeninger
S
5

Download and install the latest Google Play services from the Play Store. For some reason, I couldn't find it by searching. By running the sample app from the SDK in the folder /extras/google/google_play_services/samples/maps I was prompted to install the upgrade and was taken to the Play Store to do that.

Sponsor answered 5/12, 2012 at 11:43 Comment(5)
They are installed by default. It's a commercial (non-rooted) HTC Flyer and the rooted HTC Desire S has gapps installed (and the Google play services is there).Koeninger
You need the latest version. The error you posted refers to the default 'factory installed' version.Sponsor
I have just read here http://code.google.com/p/gmaps-api-issues/issues/detail?id=4627#c1 that by adding the new MapFragment to an active page will trigger the update. This may be easier than configuring the sample app to run if you already have a app constructed.Sponsor
You my friend are a life saver. I had for some reason disabled background data which did not update google play services automatically. Once enabled, it updated automatically and everything worked (well I don't see a map, only the +- on the bottom right, but that's another problem in itself). No need for the Fragment transaction, nor the MapsInitializer. Thanks again!Koeninger
As a general reference to visitors, this last half of this page outlines Google Play services APK installation scenarios and how to handle them. http://developer.android.com/google/play-services/setup.htmlSponsor
S
15

as described at the bottom of that link; http://developer.android.com/google/play-services/setup.html

and here comes sample code to handle this properly;

int checkGooglePlayServices =    GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
    if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
    // google play services is missing!!!!
    /* Returns status code indicating whether there was an error. 
    Can be one of following in ConnectionResult: SUCCESS, SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID.
    */
       GooglePlayServicesUtil.getErrorDialog(checkGooglePlayServices, mActivity, 1122).show();
    }
Slither answered 6/3, 2013 at 14:42 Comment(0)
S
6

I had the same problem in my project. The solution is very simple just handle the possibility that ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap(); can return null. If you will handle null the buid-in SupportMapFragment will handle Google Play services out of date. error and will show you insted of map localized message and button for updating the Google Play Service just like this that @qubz is telling about in sample project.

Code from sample:

@Override
protected void onResume() {
    super.onResume();
    setUpMapIfNeeded();
}

/**
 * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
 * installed) and the map has not already been instantiated.. This will ensure that we only ever
 * call {@link #setUpMap()} once when {@link #mMap} is not null.
 * <p>
 * If it isn't installed {@link SupportMapFragment} (and
 * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
 * install/update the Google Play services APK on their device.
 * <p>
 * A user can return to this FragmentActivity after following the prompt and correctly
 * installing/updating/enabling the Google Play services. Since the FragmentActivity may not have been
 * completely destroyed during this process (it is likely that it would only be stopped or
 * paused), {@link #onCreate(Bundle)} may not be called again so we should call this method in
 * {@link #onResume()} to guarantee that it will be called.
 */
private void setUpMapIfNeeded() {
    // Do a null check to confirm that we have not already instantiated the map.
    if (mMap == null) {
        // Try to obtain the map from the SupportMapFragment.
        mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                .getMap();
        // Check if we were successful in obtaining the map.
        if (mMap != null) {
            setUpMap();
        }
    }
}

/**
 * This is where we can add markers or lines, add listeners or move the camera. In this case, we
 * just add a marker near Africa.
 * <p>
 * This should only be called once and when we are sure that {@link #mMap} is not null.
 */
private void setUpMap() {
    mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}

And result:

enter image description here

As documentation sad

A GoogleMap can only be acquired using getMap() when the underlying maps system is loaded and the underlying view in the fragment exists. This class automatically initializes the maps system and the view; however you cannot be guaranteed when it will be ready because this depends on the availability of the Google Play services APK. If a GoogleMap is not available, getMap() will return null. SupportMapFragment

Slipcover answered 19/3, 2013 at 14:14 Comment(4)
slightly confused how to call the update for map application, like you mentioning that call of "Get Google Play Service"Lassitude
@AbdulWahab if you will handle the possibility that ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap(); can return null the google play service library will show the update screen insted of MapFragmentSlipcover
hmmm, I got it, the update screen is native call from google play service libraya, I was thinking, it getMap() return null then on a custom update button what intent I have to parse to go to update page like we have this :Intent viewIntent = new Intent("android.intent.action.VIEW", Uri.parse("stackoverflow.com/")); startActivity(viewIntent);Lassitude
@AbdulWahab Here is an explanation how you can call play service update manualy developer.android.com/google/play-services/setup.html AND developer.android.com/reference/com/google/android/gms/common/…, android.app.Activity, int)Slipcover
S
5

Download and install the latest Google Play services from the Play Store. For some reason, I couldn't find it by searching. By running the sample app from the SDK in the folder /extras/google/google_play_services/samples/maps I was prompted to install the upgrade and was taken to the Play Store to do that.

Sponsor answered 5/12, 2012 at 11:43 Comment(5)
They are installed by default. It's a commercial (non-rooted) HTC Flyer and the rooted HTC Desire S has gapps installed (and the Google play services is there).Koeninger
You need the latest version. The error you posted refers to the default 'factory installed' version.Sponsor
I have just read here http://code.google.com/p/gmaps-api-issues/issues/detail?id=4627#c1 that by adding the new MapFragment to an active page will trigger the update. This may be easier than configuring the sample app to run if you already have a app constructed.Sponsor
You my friend are a life saver. I had for some reason disabled background data which did not update google play services automatically. Once enabled, it updated automatically and everything worked (well I don't see a map, only the +- on the bottom right, but that's another problem in itself). No need for the Fragment transaction, nor the MapsInitializer. Thanks again!Koeninger
As a general reference to visitors, this last half of this page outlines Google Play services APK installation scenarios and how to handle them. http://developer.android.com/google/play-services/setup.htmlSponsor

© 2022 - 2024 — McMap. All rights reserved.