Removing delays when connecting to the GoogleApiClient

Connecting to the GoogleApiClient takes time, well at least few seconds at max. But, if you have to access it in the multiple places, that’s a lots of boiler plate code and time spent for the user and myself. I started some time before with the following implementation:

public class LocationMapFragment extends AbstractLocationFragment implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, GoogleMap.OnCameraChangeListener{

    private GoogleApiClient googleApiClient;

    @Override
    public void onStart() {
        super.onStart();

        connectToGoogleApiClient();
    }

    @Override
    public void onStop() {
        super.onStop();

        disconnectFromGoogleApiClient();
    }

    protected synchronized GoogleApiClient getGoogleApiClient() {
        if (null == googleApiClient) {
            googleApiClient = new GoogleApiClient.Builder(getActivity()).addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
        }

        return googleApiClient;
    }

    private void connectToGoogleApiClient() {
        if (getGoogleApiClient().isConnecting())
            ...
        else if (!getGoogleApiClient().isConnected())
            getGoogleApiClient().connect()
    }

    private void disconnectFromGoogleApiClient() {
        if (null != googleApiClient && (googleApiClient.isConnected() || googleApiClient.isConnecting()))
            googleApiClient.disconnect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        ...
    }

    @Override
    public void onConnectionSuspended(int i) {
        ...
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        ...
    }

}

The above code seems clean, this is how it is mentioned in the Android developer site. But, now imagine having to do this in 10 different places, you would have to write a same boiler plate code 10 times, or put it in an abstract class called AbstractGoogleApiFragment but, what if I want it in an Activity or a Service? Should I write AbstractGoogleApiActivity or AbstractGoogleApiService? Well, this is when I realized this is absurd and came to following implementation:

Wrapper for the GoogleApi callbacks

/**
 * Interface to be implemented by callers that requires to listen to callbacks from {@link GoogleApiClient}
 *
 * @author milan
 */
public interface GoogleApiListener {
    /**
     * Invoked when the {@link GoogleApiClient} is connected
     *
     * @param bundle that was passed along
     */
    void onConnected(@Nullable Bundle bundle);

    /**
     * Invoked when the {@link GoogleApiClient} has suspended
     *
     * @param reason that was passed along
     */
    void onConnectionSuspended(int reason);

    /**
     * Invoked when the {@link GoogleApiClient} connection has failed
     *
     * @param connectionResult that was passed along
     */
    void onConnectionFailed(@NonNull ConnectionResult connectionResult);
}

 

In your Application

public class App extends Application implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {
    private static final ArrayList<GoogleApiListener> API_LISTENERS = new ArrayList<>();

    private static GoogleApiClient googleApiClient;
    private static App app;

    @Override
    public void onCreate() {
        super.onCreate();

        app = this;

        connectToGoogleApiClient();
    }

    /**
     * Returns instance of this
     *
     * @return instance
     */
    public static App getInstance() {
        return app;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();

        disconnectFromGoogleApiClient();
    }

    /**
     * Builds and returns a {@link GoogleApiClient}. If already creted, will return the instance
     *
     * @return google api client.
     */
    public synchronized GoogleApiClient getGoogleApiClient() {
        if (null == googleApiClient)
            googleApiClient = new GoogleApiClient.Builder(getInstance())
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(getInstance())
                    .addOnConnectionFailedListener(getInstance())
                    .build();

        return googleApiClient;
    }

    /**
     * Connects to the google api client
     */
    private void connectToGoogleApiClient() {
        if (getGoogleApiClient().isConnected())
            getGoogleApiClient().reconnect();
        else if (!getGoogleApiClient().isConnecting())
            getGoogleApiClient().connect();
    }

    /**
     * Disconnects from the google api client
     */
    private void disconnectFromGoogleApiClient() {
        if (null != googleApiClient &amp;amp;amp;&amp;amp;amp; (googleApiClient.isConnected() || googleApiClient.isConnecting()))
            googleApiClient.disconnect();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        for (final GoogleApiListener callback : API_LISTENERS)
            callback.onConnected(bundle);
    }

    @Override
    public void onConnectionSuspended(int i) {
        for (final GoogleApiListener callback : API_LISTENERS)
            callback.onConnectionSuspended(i);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        for (final GoogleApiListener callback : API_LISTENERS)
            callback.onConnectionFailed(connectionResult);
    }

    /**
     * Attaches a given listener. If already connected, {@link GoogleApiListener#onConnected(Bundle)}
     * will be invoked right away on this specific listener.
     *
     * @param listener to attach.
     */
    public void attach(GoogleApiListener listener) {
        if (!API_LISTENERS.contains(listener))
            API_LISTENERS.add(listener);

        if (getGoogleApiClient().isConnected())
            listener.onConnected(null);
        else if (getGoogleApiClient().isConnecting())
            Log.d(TAG, &quot;services already connecting&quot;);
        else
            getGoogleApiClient().connect();
    }

    /**
     * Detaches a given listener.
     *
     * @param listener to be detached
     */
    public void detach(GoogleApiListener listener) {
        if (API_LISTENERS.contains(listener))
            API_LISTENERS.remove(listener);
    }
}

And in your Activity, Service or Fragment:

public class LocationMapFragment extends Fragment implements GoogleApiListener{

    @Override
    public void onStart() {
        super.onStart();

        App.getInstance().attach(this);
    }

    @Override
    public void onStop() {
        super.onStop();

        App.getInstance().detach(this);
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        ...
    }

    @Override
    public void onConnectionSuspended(int i) {
        ...
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        ...
    }
}

That’s it and we have reduce a massive chunk of boiler plate code if you have a large application and also our development time and our users waiting time. Last thing for us to do just to make sure that we are safe

    final GoogleApiClient apiClient = App.getInstance().getGoogleApiClient();

    if (apiClient.isConnected() &amp;&amp; apiClient.hasConnectedApi(LocationServices.API)) {
        ... LocationServices.FusedLocationApi.getLastLocation(apiClient);
    }

At this point we are done for now but, we can definitely do further refactoring. We can probably wrap our implementation in our App class to a GoogleApiManager class, that way we can use the same class if multiple projects. Let me know if you guys have came across a better implementation 🙂