Moving Past GoogleApiClient
21 November 2017
Posted by Sam Stern, Developer Programs EngineerThe release of version 11.6.0 of the Google Play services SDK moves a number of popular APIs to a new paradigm for accessing Google APIs on Android. We have reworked the APIs to reduce boilerplate, improve UX, and simplify authentication and authorization.
The primary change in this release is the introduction of new
The following APIs are newly updated to eliminate the use of
This model has a number of pitfalls:
Rather than tacking multiple APIs onto a single API client, each API now has a purpose-built client object class that extends
The call to
In general, the new
The primary change in this release is the introduction of new
Task and GoogleApi based APIs to replace the GoogleApiClient access pattern.The following APIs are newly updated to eliminate the use of
GoogleApiClient:- Auth - updated the Google Sign In and Credentials APIs.
- Drive - updated the Drive and Drive Resource APIs.
- Fitness - updated the Ble, Config, Goals, History, Recording, Sensors, and Sessions APIs.
- Games - updated the Achievements, Events, Games, Games Metadata, Invitations, Leaderboards, Notifications, Player Stats, Players, Realtime Multiplayer, Snapshots, Turn Based Multiplayer, and Videos APIs.
- Nearby - updated the Connections and Messages APIs.
The Past: Using GoogleApiClient
Here is a simple Activity that demonstrates how one would access the Google Drive API usingGoogleApiClient using a previous version of the Play services SDK:public class MyActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { private static final int RC_SIGN_IN = 9001; private GoogleApiClient mGoogleApiClient; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); GoogleSignInOptions options = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestScopes(Drive.SCOPE_FILE) .build(); mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addConnectionCallbacks(this) .addApi(Auth.GOOGLE_SIGN_IN_API, options) .addApi(Drive.API) .build(); } // ... // Not shown: code to handle sign in flow // ... @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { // GoogleApiClient connection failed, most API calls will not work... } @Override public void onConnected(@Nullable Bundle bundle) { // GoogleApiClient is connected, API calls should succeed... } @Override public void onConnectionSuspended(int i) { // ... } private void createDriveFile() { // If this method is called before "onConnected" then the app will crash, // so the developer has to manage multiple callbacks to make this simple // Drive API call. Drive.DriveApi.newDriveContents(mGoogleApiClient) .setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() { // ... }); } }The code is dominated by the concept of a connection, despite using the simplified "automanage" feature. A
GoogleApiClient is only connected when all APIs are available and the user has signed in (when APIs require it).This model has a number of pitfalls:
- Any connection failure prevents use of any of the requested APIs, but using multiple
GoogleApiClientobjects is unwieldy. - The concept of a "connection" is inappropriately overloaded. Connection failures can be result from Google Play services being missing or from authentication issues.
- The developer has to track the connection state, because making some calls before
onConnectedis called will result in a crash. - Making a simple API call can mean waiting for two callbacks. One to wait until the
GoogleApiClientis connected and another for the API call itself.
The Future: Using GoogleApi
Over the years the need to replaceGoogleApiClient became apparent, so we set out to completely abstract the "connection" process and make it easier to access individual Google APIs without boilerplate.Rather than tacking multiple APIs onto a single API client, each API now has a purpose-built client object class that extends
GoogleApi. Unlike with GoogleApiClient there is no performance cost to creating many client objects. Each of these client objects abstracts the connection logic, connections are automatically managed by the SDK in a way that maximizes both speed and efficiency.Authenticating with GoogleSignInClient
When usingGoogleApiClient, authentication was part of the "connection" flow. Now that you no longer need to manage connections, you should use the new GoogleSignInClientclass to initiate authentication:public class MyNewActivity extends AppCompatActivity { private static final int RC_SIGN_IN = 9001; private GoogleSignInClient mSignInClient; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); GoogleSignInOptions options = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestScopes(Drive.SCOPE_FILE) .build(); mSignInClient = GoogleSignIn.getClient(this, options); } private void signIn() { // Launches the sign in flow, the result is returned in onActivityResult Intent intent = mSignInClient.getSignInIntent(); startActivityForResult(intent, RC_SIGN_IN); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) { Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data); if (task.isSuccessful()) { // Sign in succeeded, proceed with account GoogleSignInAccount acct = task.getResult(); } else { // Sign in failed, handle failure and update UI // ... } } } }
Making Authenticated API Calls
Making API calls to authenticated APIs is now much simpler and does not require waiting for multiple callbacks.private void createDriveFile() { // Get currently signed in account (or null) GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this); // Synchronously check for necessary permissions if (!GoogleSignIn.hasPermissions(account, Drive.SCOPE_FILE)) { // Note: this launches a sign-in flow, however the code to detect // the result of the sign-in flow and retry the API call is not // shown here. GoogleSignIn.requestPermissions(this, RC_DRIVE_PERMS, account, Drive.SCOPE_FILE); return; } DriveResourceClient client = Drive.getDriveResourceClient(this, account); client.createContents() .addOnCompleteListener(new OnCompleteListener<DriveContents>() { @Override public void onComplete(@NonNull Task<DriveContents> task) { // ... } }); }Before making the API call we add an inline check to make sure that we have signed in and that the sign in process granted the scopes we require.
The call to
createContents() is simple, but it's actually taking care of a lot of complex behavior. If the connection to Play services has not yet been established, the call is queued until there is a connection. This is in contrast to the old behavior where calls would fail or crash if made before connecting.In general, the new
GoogleApi-based APIs have the following benefits:- No connection logic, calls that require a connection are queued until a connection is available. Connections are pooled when appropriate and torn down when not in use, saving battery and preventing memory leaks.
- Sign in is completely separated from APIs that consume
GoogleSignInAccountwhich makes it easier to use authenticated APIs throughout your app. - Asynchronous API calls use the new
TaskAPI rather thanPendingResult, which allows for easier management and chaining.