Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 673
iliask Avatar asked Dec 05 '12 11:12

iliask


People also ask

Is the googlemap object provided by the callback non-null?

The GoogleMap object provided by the callback is non-null. The callback object that will be triggered when the map is ready to be used. Must not be null. Creates a map fragment with the given options.

How to remove a view from a googlemap?

A GoogleMap must be acquired using getMapAsync (OnMapReadyCallback). This class automatically initializes the maps system and the view. A view can be removed when the SupportMapFragment's onDestroyView () method is called and the GoogleMapOptions.useViewLifecycleInFragment (boolean) option is set.

What if the googlemap is destroyed immediately after creation?

In the rare case where the GoogleMap is destroyed immediately after creation, the callback is not triggered. The GoogleMap object provided by the callback is non-null. The callback object that will be triggered when the map is ready to be used.


2 Answers

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();
    }
like image 50
Yilmaz Guleryuz Avatar answered Nov 15 '22 22:11

Yilmaz Guleryuz


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

like image 38
Roman Nazarevych Avatar answered Nov 15 '22 22:11

Roman Nazarevych