Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: How to sync application data stored in Room database between devices of the same user?

I am working on an app that stores user's data locally. I have used the Room database to store the user-created data in an Android device. Now if the user has more then one device then the data should be available on all their (Android) devices without any central database or web services.

Note: for the iOS side I used CloudKit to sync data between iOS devices. Is there any way to do the same for Android devices? Please note I don't want to store the data on any central database or webserver or any central cloud storage. It should be owned by the user only. (i.e. it should be synced via the user's Google account). Note: I want a native android solution and cross-platform (Android to iOS) sync is not required.

Update: Till now I found some options like Firestore and Realtime database and some AWS services also provide the sync facility but the issue with all these is that they all store the user data at a central cloud location.

So my question still remains the same - Is there a way to sync data between android devices using Google Drive or something similar where we can utilize the user's own cloud storage rather than storing user data in a central database.

like image 723
Bharat Avatar asked Apr 11 '20 10:04

Bharat


People also ask

How sync happens between a web app and Android app of the same project?

SyncAdapter: The sync adapter component in your app encapsulates the code for the tasks that transfer data between the device and a server. Based on the scheduling and triggers you provide in your app, the sync adapter framework runs the code in the sync adapter component.

What is the use of room database in Android?

Room Database is a part of the Android Architecture components which provides an abstraction layer over SQLite which allows for more robust database access while still providing the full power of SQLite.

What is offline synchronization in Android?

Offline sync allows end users to interact with a mobile app—viewing, adding, or modifying data—even when there's no network connection. Changes are stored in a local database. Once the device is back online, these changes are synced with the remote backend.


1 Answers

Edit

There is no native/default/out of the box mechanism to sync data in real time on Android

There are surrogates though.

In context of Android two major options appear:

  • Sockets with some persistent storage - you may implement backend server with support of sockets and add this support for your android(ios/web/desktop) app. This is the most common and pretty low level mechanism to implement real time sync between devices. Pros : Universal across platforms, industry standard, a lot of info online. Cons: as it is relatively low level thing a lot of code is required, may be quite complex and complicated if there are a lot of sync evens, needs backend, needs separate persistence. Major implementations: Socket.io

  • Firebase Realtime Database/ Cloud Firestore - basically wrapper around sockets in conjunction with some database. "Pros": Doesn't require neither backend nor database, free for prototype/low-load apps, pretty straight forward implementation, cross platform, works pretty well. Cons : A lot of boilerplate if you want to go "clean way", you have no control over the inner processes, not very obvious payment calculation on high loads, you link yourself with a third party backend with specific API - well defined architecture required if you will want to move from it somewhere else without complete app remake. comparison: firebase cloud firestore vs realtime database

I will not get into details with the implementation because the official documentation is quite straightforward in this case(unlike with google drive)

Cloud Firestore documentation

Realtime Database documentation

Edit end

Yes. There is a possibility to privately store data using Google Drive. It will be accessible only by your application. Moreover it won't occupy any space in users Google Drive which is pretty handy. Cross platform in this case possible but it needs to be implemented separately for iOs and Android(not supported out of the box by SDK).

So the solution is based on Google Drive API and more specifically on Store application-specific data part of it.

The only visible for the user action regarding this will be google login prompt.

Here is a detailed quickstart for Java

So in order to do it you will need:

  • Add dependencies;

  • Implement google auth like described here and here

  • Very important Authenticate users with drive.appdata and drive.file scopes. Also enable Google Drive API in your Google Cloud Console. All this is described here

  • Figure out where your Room SQLite file is situated like this: String currentDBPath = context.getDatabasePath("your_database_name.db").getAbsolutePath();

  • Upload file to the Google Drive App Folder like it is described here

Basically it will look like this

  • Enable Google Dive API in Console

  • Add dependencies(maybe need to be updated to relevant versions)

implementation 'com.google.android.gms:play-services-auth:17.0.0'// for google sign in

// for drive integration
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
implementation('com.google.api-client:google-api-client-android:1.26.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') 
{
exclude group: 'org.apache.httpcomponents'
} 
  • In android gradle tag
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
}
  • Manifest
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  • Google auth with required scopes
       val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(
                 Scope(Scopes.DRIVE_FILE),
                 Scope(Scopes.DRIVE_APPFOLDER)
            )
            .requestEmail()
            .build()
        googleSingInClient = GoogleSignIn.getClient(this, gso)
        val signInIntent = googleSingInClient.signInIntent
        startActivityForResult(signInIntent, RC_SIGN_IN)
        ... // handle sign in result
  • Set up google drive client service(where context is)
        val mAccount = GoogleSignIn.getLastSignedInAccount(this)

        val credential =
            GoogleAccountCredential.usingOAuth2(
                applicationContext, Collections.singleton(Scopes.DRIVE_FILE)
            )
        credential.setSelectedAccount(mAccount.getAccount())
        val googleDriveService =
            Drive.Builder(
                AndroidHttp.newCompatibleTransport(),
                GsonFactory(),
                credential
            )
                .setApplicationName("Your app name")
                .build()
  • Create and send file
        val fileMetadata = File()
        fileMetadata.setName("your_database_name.db")
        fileMetadata.setParents(Collections.singletonList("appDataFolder"))
        val databasePath = context.getDatabasePath("your_database_name.db").absolutePath
        val filePath = File(databasePath)
        val mediaContent = FileContent("application/sqlite", filePath)
        val file = driveService.files().create(fileMetadata, mediaContent)
            .setFields("id")
            .execute()
  • Search file on other device
        val files = driveService.files().list()
            .setSpaces("appDataFolder")
            .setFields("nextPageToken, files(id, name)")
            .setPageSize(10)
            .execute()
        for (file in files.getFiles()) {
            // get file here 
        }

A bit more info you may get here

Hope it helps.

like image 75
Pavlo Ostasha Avatar answered Sep 30 '22 04:09

Pavlo Ostasha