Getting a new Android smartphone or tablet is exciting, but it’s also a risky time for application developers. It’s easy to lose your audience when they switch to a new device.

Wherever possible, the Android system automatically downloads the user’s previously-installed applications during device setup, so your app will typically follow the user to their new device. But what about all the data the user has amassed in your application?

Your typical Android application contains a wealth of information users will want to bring with them, ranging from their username and password, to any changes they’ve made to the settings, and even documents and images created within your application.

When the user launches your app on their shiny new Android device, they should be able to pick up exactly where they left off, rather than discovering all the time and effort they’ve invested in your application has been completely lost!

Let’s look at several ways you can store and then restore that all-important user data, including an easy way of backing up all your app’s data to the cloud, and an API that keeps users logged into your app, even if they switch to a new device.

Saving your application’s data to Google Drive

In Android 6.0 and higher, you can use Auto Backup to save 25MB of your app’s data to a private folder in the user’s Google Drive account, without contributing to their Google Drive quota. Whenever your app is reinstalled, this data will be restored from Google Drive automatically.

Auto Backup is the easiest way to backup application data and many apps already use it. To see which applications are already using this feature on your own Android device:

  • Launch the Google Drive app.
  • Drag to open the side-menu, and then select “Backups.”
  • Select the most recent backup from the list.
  • Tap “App data,” which will reveal a list of every app that’s backing up data to your Google Drive account.

If your app targets Android 6.0 or higher, then Auto Backup is enabled by default, as the android:allowBackup attribute defaults to true. However, there’s never any guarantee Android’s default behavior won’t change in a future release, so you should always be explicit about the features your application supports.

To make it clear your app supports Auto Backup, add this to your Manifest:

<application
   android:allowBackup="true"
...
...
...

Do you need to include everything in your backups?

By default, Auto Backup will store almost all of your app’s content, including shared preference files, custom data saved to your app’s internal storage, and persistent files saved to external storage.

However, occasionally you may need to manually exclude some content from your Auto Backups:

  • Any content that features sensitive user information. Due to customizations made by device manufacturers, the backup transport used to store and retrieve Auto Backup data can differ between devices, which makes it difficult to guarantee the security of your Auto Backup data.
  • Any content with a unique identifier, such as Google Cloud Messaging (GCM) registration IDs. If Auto Backup restores this kind of content on a new device, the identifiers will be outdated and your app may encounter problems when it tries to use this content.

If you need to specify what data’s stored by Auto Backup, you can create a file of include/exclude rules:

  • If your project doesn’t already contain a res/xml directory, then control-click its “res” folder and select New > Android resource directory. Name this folder ‘”ML” and then click “OK.”
  • Control-click your project’s res/xml directory and then select New > XML resource file.
  • Name this file backup_rules and then select “OK.”

Open this file and create your rules:

<?xml version="1.0" encoding="utf-8"?>

//Your rules must start with a <full-backup-content> element//

<full-backup-content>

//Specify the file(s) or folder(s) that you want to include in your backups//

    <include domain="sharedpref" path="."/>

//Specify the file(s) or folder(s) that you want to exclude from your backups//

 <exclude
       domain="sharedpref"
       path="com.jessicathornsby.test.LOGIN_DETAILS.xml"/>

</full-backup-content>

You specify the location of each file or folder, using the “domain” attribute. In the above example, both items are located in sharedpref, but there are several other values you can use:

  • domain=”root.” The directory where all your application’s private files are stored.
  • domain=”file.” The directory returned by getFilesDir().
  • domain=”database.” The directory returned by getDatabasePath(), including databases created with SQLiteOpenHelper.
  • domain=”external.” The directory returned by getExternalFilesDir().

When creating your rules, there are a few points to bear in mind:

  • Unless you state otherwise, Auto Backup will include almost all your application’s data in its backups. As soon as you create an include rule, it’ll only backup the files specified by you. To ensure important data doesn’t get left out of your backups, you should only create include rules when it’s really important.
  • Auto Backup always excludes the directories returned by getCacheDir(), getCodeCacheDir() and getNoBackupFilesDir(). Even if you create include rules for these directories, Auto Backup will ignore your request.

Once you’ve created your rules, you just need to reference this file in your project’s Manifest:

<application
   android:fullBackupContent="@xml/backup_rules"
...
...
...

Testing your app’s Auto Backup support

Backups occur automatically whenever all these conditions are met:

  • Auto Backup is enabled on the device. You can toggle Auto Backup on and off by opening your device’s “Settings” application and then selecting Cloud and accounts > Backup and restore > Back up my data.
  • At least 24 hours have passed since the last backup.
  • The application data has changed since the previous backup.
  • The device is idle and charging, with an active Wi-Fi connection.

Typically, this equates to around one backup per day, but when testing your app you don’t have to wait 24 hours for a backup to occur naturally! You can test your app’s Auto Backup support on-demand, using adb (Android Debug Bridge) commands, which run from the Terminal (Mac) or Command Prompt (Windows).

You’ll find the .adb program in your Android/sdk/platform-tools folder, so open a Terminal/Command Prompt Window and “change directory” to point at the platform-tools folder:

cd /Users/jessicathornsby/Library/Android/sdk/platform-tools

Next, make sure you’ve enabled Auto Backup and registered a Google account on the device or emulator you’re using to test your app.

To verify Auto Backup has restored its data successfully, you’ll need to generate some user data, like a username or password into your app.

Once you’re ready to create a backup, run the following command in the Terminal or Command Prompt window:

./adb shell bmgr backupnow <YOUR-APP’S-PACKAGE-NAME>

After a few moments, the command should return this:

Backup finished with result: Success

To restore this backup, uninstall your application and then reinstall it. When your app launches, all the data included in the backup should already have been restored.

Transfer usernames and passwords to a new device

If your app has any kind of sign-in experience, it should remember the user’s login details, even when they switch to a new device.

Unlike web browsers where users may periodically delete their history and cache, mobile users tend to log into an application once and then stay logged in.

When you’re excited to use a new device, the last thing you want to do is remember application passwords you haven’t typed in years. There are several ways that your app can recover user credentials and then log the user in automatically, even when they switch to a new device.

Implement Google Sign-In

Google Sign-In lets people log into your application using their Gmail address and password.

Implementing Google Sign-In in your application is particularly effective, as many Android devices ask users for their Google account details as part of the device setup dialog. By the time the user reaches your application, there’s a high chance they’ll have already stored their Google account details on their new device.

If the user has enabled automatic login, you may even be able to log them in automatically the very first time they launch your app. Even if the user hasn’t activated automatic login, Google Sign-In makes logging into your application as simple as tapping a “Sign in with Google” button.

To implement Google Sign-In, create a Google API Console project, then open your project’s build.gradle file and add Google Play Services as a project dependency:

    dependencies {
        implementation 'com.google.android.gms:play-services-auth:11.8.0'
    }

Google provides a standard “Sign in with Google” button, too:

<com.google.android.gms.common.SignInButton
 android:id="@+id/sign_in"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

Users typically log into a mobile app once and then remain logged in, so you should always check whether the user is currently signed into your application:

    @Override
    public void onStart() {
        super.onStart();
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        updateUI(account);
    }

If GoogleSignIn.getLastSignedInAccount returns null, then the user isn’t logged into your application, and you should give them the option to log in using their Google account:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

//Create a GoogleSignInOptions object//

GoogleSignInOptions gso = new

//Specify the information your app requires. DEFAULT_SIGN_IN includes the user’s ID and basic profile//

GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)

//Request the user’s email address//

        .requestEmail()
        .build();

//Build a GoogleSignInClient//

mGoogleSignInClient = GoogleSignIn.getClient(this, gso);

   }

Whenever the user taps the “Sign In With Google” button, you should start the sign-in Intent:

findViewById(R.id.sign_in).setOnClickListener(this);
...
...
...

private void signIn() {

//Create a sign-in intent//

    Intent signInIntent = mGoogleSignInClient.getSignInIntent();

//Start the sign-in intent with startActivityForResult//

    startActivityForResult(signInIntent, RC_SIGN_IN);
}

Next, handle the Activity result:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_SIGN_IN) {

//Since Task is completed immediately, you don’t need to attach an asynchronous listener//

        Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
        handleSignInResult(task);
    }
}

private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
    try {
      GoogleSignInAccount account = completedTask.getResult(ApiException.class);

//If the user is signed in successfully, then update your app’s UI//

        updateUI(account);
    } catch (ApiException e) {

//If sign in failed, then log the status code for this failure//

        Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
        updateUI(null);
    }
}

 private void updateUI(@Nullable GoogleSignInAccount account) {
        if (account != null) {

//Once the user is signed in, do something, for example hide the ‘Sign In’ button//
//TO DO//
} else {
...
...
...
   }

}

Store your password in the cloud with Smart Lock

Smart Lock for Passwords syncs the user’s passwords with their Google account. By adding Smart Lock support to your application, you can store users’ passwords in the cloud, and retrieve them automatically on all subsequent logins, rather than displaying a “Sign In” screen. Assuming a user signs in with the same Google account on their new device, your app’s password will be automatically available on this new device.

To add Smart Lock for Passwords support to your app, you’ll need to add Google Play Services as a project dependency:

dependencies {
  implementation 'com.google.android.gms:play-services-auth:11.8.0'

Next, you’ll need to retrieve the user’s credentials from the cloud. This requires us to implement GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener so our app can handle successful and failed connection attempts:

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

//Access the Credentials API, by creating an instance of CredentialsClient//

 GoogleApiClient mCredentialsClient;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

//Instantiate GoogleApiClient//

        mCredentialsClient = new GoogleApiClient.Builder(this)

//Get a notification whenever the client has connected successfully//

              .addConnectionCallbacks(this)
              .addOnConnectionFailedListener(this)
              .enableAutoManage(this, this)
              .addApi(Auth.CREDENTIALS_API)
              .build();
}

  @Override
    public void onConnected(Bundle bundle) {
        Log.d(TAG, "onConnected");
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.d(TAG, "onConnectionSuspended");
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.d(TAG, "onConnectionFailed");
    }

Then create a CredentialRequest object:

mCredentialRequest = new CredentialRequest.Builder()
    .setPasswordLoginSupported(true)
    .setAccountTypes(
               IdentityProviders.GOOGLE)

    .build();

Now that you’re connected, request any credentials available for your application:

//Pass the request object to the CredentialsClient.request() method//

mCredentialsClient.request(request).addOnCompleteListener(
      new OnCompleteListener<CredentialRequestResponse>() {
          @Override
          public void onComplete(@NonNull Task<CredentialRequestResponse> task) {

              if (task.isSuccessful()) {

//If the credential is retrieved successfully, then call onCredentialRetrieved//

                onCredentialRetrieved(task.getResult().getCredential());
                return;
             }

//If no credential was received...//

//TO DO//

          }
      });

If a credential is received, use this information to sign the user into your application:

private void onCredentialRetrieved(Credential credential) {

//Check the type of credentials that your app has received//

    String accountType = credential.getAccountType();
    if (accountType == null) {
        signInWithPassword(credential.getId(), credential.getPassword());
    } else if (accountType.equals(IdentityProviders.GOOGLE)) {

        GoogleSignInOptions gso =
                new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                    .requestEmail()
                    .build();

//To log in with Google, create a GoogleSignInClient object and then start the sign-in flow//

        GoogleSignInClient signInClient = GoogleSignIn.getClient(this, gso);
        Task<GoogleSignInAccount> task = signInClient.silentSignIn();
...
...
...
    }
}

If the user signs in with a new set of credentials, your app needs to store this information so it can be retrieved on subsequent logins:

Credential credential = new Credential.Builder(email)
        .setPassword(password)
        .build();

mCredentialsClient.save(credential).addOnCompleteListener(
        new OnCompleteListener() {
            @Override
            public void onComplete(@NonNull Task task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Credentials saved");
                    return;

At this point, your application will ask the user to confirm they want to save this password to Smart Lock, so your final task is handling the user’s response:

  @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" +
         data);
   if (requestCode == RC_SAVE) {
       if (resultCode == RESULT_OK) {
        Log.d(TAG, "Credential saved");
     } else {
          Log.e(TAG, "Credential save cancelled by user");
        }
    }

}

Transfer accounts over Bluetooth

If the user logs into your application using a unique username and password, you can copy this information from their previous device, to their new device as part of the device setup process, using Android’s Account Transfer API.

This API creates a connection between the user’s previous (source) device and their new (target) device, and transfers your application’s login credentials over an encrypted Bluetooth connection, or via a phone-to-phone USB cable if the new device happens to be a Pixel.

To use the Account Transfer API, you need to add Google Play Services 11.2.0 or higher to your project:

dependencies {
   implementation 'com.google.android.gms:play-services-auth:11.8.0'

Next, you’ll need to update your project’s Manifest to listen for the various broadcasts associated with the Account Transfer API.

When the user opts to transfer data, the source device will send an ACTION_START_ACCOUNT_EXPORT broadcast for which your application will need to listen:

<receiver android:name=".MyBroadcastReceiver"
 android:enabled="true"
            android:exported="true" >
            <intent-filter>
               <action android:name="com.google.android.gms.auth.START_ACCOUNT_EXPORT" />
            </intent-filter>

If data is available to be imported, then your app will receive the ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE broadcast:

<intent-filter>
   <action android:name="com.google.android.gms.auth.ACCOUNT_IMPORT_DATA_AVAILABLE" />
</intent-filter>

You’ll also need to register to listen for the ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE broadcast, which will be received by the source device:

<intent-filter>
   <action android:name="com.google.android.gms.auth.ACCOUNT_EXPORT_DATA_AVAILABLE" />
</intent-filter>
     </receiver>

To send account data from a source device, you’ll need to start an authenticator service and call sendData() in response to the ACTION_START_ACCOUNT_EXPORT broadcast.

//Get a reference to an AccountTransferClient object//

AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
Task<Void> exportTask = client.sendData(ACCOUNT_TYPE, transferBytes);
try {
  Tasks.await(exportTask, TIMEOUT_API, TIME_UNIT);
} catch (ExecutionException | InterruptedException | TimeoutException e) {

//Once the transfer is complete, call notifyCompletion with the appropriate completion status//

client.notifyCompletion(ACCOUNT_TYPE,AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
  return;
}

The setup wizard on the target device will then receive the account data.

Upon receiving the ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE broadcast, your app will need to start a service, calling retrieveData() to retrieve data from the source device.

AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
Task<Void> exportTask = client.retrieveData(ACCOUNT_TYPE);
try {
  byte[] transferBytes = Tasks.await(transferTask, TIMEOUT_API, TIME_UNIT);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
  client.notifyCompletion(ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
  return;
}
client.notifyCompletion(ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);

Wrapping up

We only looked at how to restore and preserve application data using Auto Backup, Google Sign-In, Smart Lock, and the Account Transfer API, but there’s lots of different ways do it.

Do you use any techniques not mentioned in this article? Let us know in the comments below!