Is it possible to build offline-first mobile apps using AWS AppSync? - android

I'd like to use AWS AppSync for mobile development (Android/iOS) but I’m not sure about its offline capabilities.
According to the documentation the data will be accessible while being offline and synced automatically if the client gets online again. But I can't find any information about if the app client needs to connect to AWS first, before using AppSync to create and modify offline data.
I'm not familiar with the underlying technologies of AppSync (e.g. GraphQL) and I don't have access to the public preview version to test it myself.
I would like to enable privacy-sensitive users to use an app without connecting to AWS while still being able to use AppSync as an offline database. Only if a user later decides to use backup/sync data across devices he or she can opt-in to connect to AWS.
Will this use case be possible with AWS AppSync?
Without using any other local storage (like SharedPreferences, SQLite, Realm, etc.)

It should be possible with Firestore, AWS AppSync or your own Backend solution. Any approach you use, you will control when you want to start saving/syncing things online.
You need to handle all this while designing this app. Suggested approach
Let's take example of simple ToDo app
I will let User add & save Todos in app
All this data will be persisted on disk(using SQLLITE, Preferences or File etc.)
If User does clear data or reinstall app, all this data is lost
If User wants to go premium, I will let him sync this data with my Backend solution(any one of above-mentioned solution)
Example implementation using Android Shared preference as local storage
public void saveLocalTodo(String title, String details) {
ArrayList<Todo> todos;
Todo todo = new Todo(title, details);
String listOfTodo = sharedPreference.getString(TODOS_LIST, null);
if (listOfTodo == null)
todos = new ArrayList<Todo>();
else
todos = gson.fromJson(listOfTodo, new TypeToken<ArrayList<Todo>>() {
}.getType());
//save at 0th position, recent should always come first
todos.add(0, todo);
sharedPreference.edit().putString(TODOS_LIST, gson.toJson(todos)).apply();
}
public ArrayList<Todo> getLocalTodos() {
ArrayList<Todo> todos;
String listOfTodos = sharedPreference.getString(TODOS_LIST, null);
if (listOfTodos == null)
todos = new ArrayList<Todo>();
else
todos = gson.fromJson(listOfTodos, new TypeToken<ArrayList<Todo>>() {
}.getType());
return todos;
}
public void saveOnBackend() {
// Connect to Backend solution
// Get all local todos from preference
// Save all at once in batches
//OR
// Get all local todos from preference
// Save one by one
}

Use Realm Database to manage all offline and online data and save if the application uninstall

you can read
https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-reactnative.html
AWS AppSync support offline mode
and you can use data base for your app

Related

Firestore offline cache

I'm building an Android application which has to work offline for weeks, but can sync immediately with a remote DB as it goes online.
My question is can Firestore be a good option for this? How long does Firestore keep its offline cache?
Firestore can be configured to persist data for such disconnected/offline usage. I recommend that you read the enable offline persistence section in the docs, which contains this sample of enabling this feature:
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(true)
.build();
db.setFirestoreSettings(settings);
This persistence is actually enabled by default on Android and iOS, so the call above is not needed.
Your Android code that interact with the database will be the same whether you're connected or not, since the SDK simply works the same. If you want to detect whether data is coming from the cache (and thus potentially stale), read the section Listen to offline data in the docs.
The data in the cache does not expire after a certain amount of time. The only two reasons data is removed from the cache:
The data has been removed from the server, in which case the client will remove it from the disk cache.
The client needs to purge its disk cache to make space for more recent data.
EDIT-25/5/2020: Francesco is correct, the docs link given in the comment does clarify that. It seems the cache size has been changed, by default it has been decreased to 40MB.
OLD: The following answer follows the official guide in the following link:
Handling Cache size
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED)
.build();
db.setFirestoreSettings(settings);
The above code has a flag set for setCacheSize(), which will prevent your cache from
being cleared. You can also specify the same in size. If you do not set this by default the size is 100MB.
As per the guide, there is a method to check if the data you query came from cache or the firestore. Also the moment your device is back online the firestore refreshes the cache, and keeps the data synchronized.
To answer your question, as you have to work with offline data for weeks, i suggest every time the data is fetched to store it in json/xml formats, as storing huge amount of data in cache is not a really good approach when thought of in terms of performance.
I hope i helped you clear some things out.
If you listen to data in Cloud Firestore, you will get immediate snapshots of cached data and also updates when your app is able to connect online:
final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
// Determine if the data came from the server or from cache
String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
? "Local" : "Server";
// Read the data
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, source + " data: " + snapshot.getData());
} else {
Log.d(TAG, source + " data: null");
}
}
});
Persistence is enabled by default so this behavior does not require any configuration.

Eventual problems getting response in mobile clients after doing insert from azure mobile service

I have an app that runs on android mobile smartphones and connects to azure mobile service backend (based on node js). Basically what I do is insert data to a table in the mobile service database. In the mobile app, after inserting my object I check if the response object has an id different to null, in that case I delete the row in the local sqlite database, if id equals "null" then I retry the operation later. The problem is that frequently I don't receive the id, so that id="null" and I retry operation, nevertheless when fetching the azure database the row was successfully inserted.
Here is part of my code:
if (downeventstable == null && !error)
downeventstable = FirstmenuScreen.mClient.getTable("downevents" + servicio, downevents.class);
boolean error2 = true;
try {
downeventstable.insert(dwnevent).get();
error2=false;
} catch (Exception e) {
error2 = true;
break;
}
if (!error2 && dwnevent.getId() != null) {
// if no error and id was received deletes row locally in sqlite
}
I guess that the problem is caused by a bad quality of the mobile network, maybe the app lost connection after a successfull insertion in azure and didn't get the response object. How may I know if the data was inserted in azure and treat these situations?
If your insert was successful and you try to re-insert the same record with the same ID later, you'll get a 409 conflict. You can handle this in your sync conflict handler if you're using offline sync, or just exception handling if you're using a regular table.
Here's a Xamarin sample that handles the 409 conflict, which is MobileServiceConflictException in C#. You would do something similar in Java. You can test this out by using a REST client and seeing that you get a 409.
Btw, it sounds like you are doing a lot of manual work with your own SQLite tables. You might want to consider using the offline sync feature that is built in, which will do the retries for you: Add Offline Data Sync to your Android Mobile Services app.
Note that Mobile Services is being deprecated, so we recommend that you upgrade your code to Mobile Apps if it is a new app. See Announcing Azure Mobile Services transition to Azure App Service.

Have Android app users create and change objects on Google Cloud Storage

I am looking for a strategy to have global objects that can be accessed across all users on all devices. My idea is to create an object or file and put it on Google Cloud Server, and using Datastore, Blobstore, or Cloud Storage (or maybe something else?), and have the object/file change as different users interact with it and alter variable values.
Now, how in the world can I do this - I am having a lot of trouble understanding the documentation that Google offers. Are there any convenient APIs for this? If so, how can these API's be accessed? Currently, I have followed the Android Studio "Hello Endpoints" setup, and I have a working backend module running on AppEngine.
So far I have learned how to create an API and API methods:
#Api(
name = "myApi",
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "backend.myapplication.Mike.example.com",
ownerName = "backend.myapplication.Mike.example.com",
packagePath = ""
)
)
public class MyEndpoint {
/**
* A simple endpoint method that takes a name and says Hi back
*/
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String name) {
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
So this shows I have successfully created a Cloud Endpoints / backend that goes with an App Engine project.
So, what I was hoping to do with this set up is to create an API method to save data to the cloud server, and then have other users on other devices to be able to retrieve that data.
If you're looking for something like "a big shared object" that lots of people can all change collaboratively, you might want to check out Firebase, which does this and keeps everyone in sync (and integrates nicely with Android and iOS).

How does an Android and App Engine Actually communicate?

Ok, this may seem as a dumb question, but I am really new when it comes to Cloud Computing/Google App Engine etc. In order to get more familiarized with it, I started to work with some tutorials from developers.google.com, basically following the tutorials and then trying to perform small changes to the provided pieces of code, in order to make sure that I actually understood the way it works, not just copy/paste and take everything for granted.
The problem is that I got a little bit stuck at the following aspect: the way Android and App Engine actually communicate. I am currently doing this tutorial(
https://developers.google.com/eclipse/docs/endpoints-addentities). The problem is the following piece of code (client-side, on Android):
public class EndpointsTask extends AsyncTask<Context, Integer, Long> {
protected Long doInBackground(Context... contexts) {
Noteendpoint.Builder endpointBuilder = new Noteendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
Noteendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
try {
Note note = new Note().setDescription("Note Description");
String noteID = new Date().toString();
note.setId(noteID);
note.setEmailAddress("E-Mail Address");
Note result = endpoint.insertNote(note).execute();
} catch (IOException e) {
e.printStackTrace();
}
return (long) 0;
}
}
As far as my understanding helps me, at this moment, in terms of Cloud Computing, I inferred that the communication between Android and the Cloud is performed via endpoint object, where endpoint is:
Noteendpoint endpoint = CloudEndpointUtils.updateBuilder (endpointBuilder).build();
Also, the updateBuilder() method looks like this:
public static <B extends AbstractGoogleClient.Builder> B updateBuilder(
B builder) {
if (LOCAL_ANDROID_RUN) {
builder.setRootUrl(LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID
+ "/_ah/api/");
}
// only enable GZip when connecting to remote server
final boolean enableGZip = builder.getRootUrl().startsWith("https:");
builder.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
public void initialize(AbstractGoogleClientRequest<?> request)
throws IOException {
if (!enableGZip) {
request.setDisableGZipContent(true);
}
}
});
return builder;
}
I do understand that the insertion in the Data Storage is performed via insertNote() - which, basically, performs basic a standard insert method.
My problem is that I cannot really understand where, in the cloud, the information that I've sent from my Android device is caught. To be more specific, I am sending an object, and I cannot really see where that object is received in the Cloud. Probably at this kind of basic application, is not that relevant, but I want to develop an application with the following structure: I am sending data from my Android device using REST. I am developing my server-side code )(which will be in the Cloud). In my server-side code will receive the data I am sending from Android -> process that data -> add something in the database (database stored in the cloud) (this is the basic principle explained in VERY primitive terms). That's why I really want to understand the way this works and so far, I really cannot see where my data is received on the server side. I have assumed that there is probably some automatic mechanism behind this ? If so, I am really interested, if you could indicate me, how can I do that programatically.
Also, I would like to mention that this code works really good, so there are no errors in it, I just have problems in understanding all the details related to it.
Thank you.
LATER EDIT:
My database will be App Engine Datastore. The main problem is that I cannot really understand the way the communication between my Android Application and the Google App Engine Application (where I will be making all the necessary computations with the data I receive from Android) is made. I could really use a more "obvious"/explainatory (for dummies) piece of code where I actually see that the object I send from Android is received in the Google App Engine Application. Of course, I saw the result, using Datastore Viewer, which shows that the data is inserted in the database. What interests me is how I can actually just send the data in my Google App Engine Application, receive it there and perform some operations on it, and ONLY after I will add it in the database.
The updateBuilder() method is not on the server side. It's part of the android code. CloudEndpointUtils is part of android. It's a class you create to handle the boilerplate code for you so you don't have to type it each time you need to access the server. You see the code
Noteendpoint.Builder endpointBuilder = new Noteendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
It's assuming your api is called Noteendpoint and that you are "building" an object to access it. You could have called endpointBuilder.build() to start querying your api. If you look in the CloudEndpointUtils.updateBuilder method closely, you will see that what it is doing is redirect your calls to your localhost as opposed to your deployed code on appengine.
Let me know if you need clarification.
TO ANSWER YOUR EDIT:
Forget for a moment that this is a Google-endpoint application. You are basically designing a system that takes inputs, work on the inputs, and then save the result to a database.
For now let's pretend it does not matter whether the input is coming from a terminal or a file or a call to an api endpoint. What you absolutely must do is create a layer that will manipulate the data and then pass that data to your persistence layer (ie database). So you will need (still ignoring all things Google/appengine):
JPA entity (POJO with getters and setters and JPA annotations only)
Data access layer: This is a class with methods to perform queries on your JPA POJO (using EntityManagerFactory).
Business layer: This is a class where you manipulate the data you receive, then pass the result to the data access layer.
Hence you have Business logic layer => Data access layer => JPA POJO. So create all that not worring whether it's going to run on your local glassfish or wherever.
AFTER you are done, add endpoints annotations to your Business layer methods. Those annotations basically means that after you generate your android endpoint library, your android will be able to call your Business layer methods as if they were right inside your android application codes.
You get it? It will be as if your android and your server were one. Are you using the Google Eclipse plugin for this project?
I really don't understand your question. The "cloud" is represented by a Google App Engine application. That lives on Google's servers, and is served under the URL that you assign when you create the project. The database will presumably be either the App Engine datastore, or the Google Cloud SQL service which uses a version of MySQL.

How to use a server to store/receive data?

I am wondering what is required to setup a server so that you can store data on it, and then have an application send requests to it to store and receive data. More specifically, I am working on an Android application where a user will generate data and then that should be stored on a server so other users can access it. But I do not know how setting up a server to be capable of this works. I have worked on Android applications in the past that sends requests (put, post, get, etc) to a server, but that back end was already set up for me. Any info or resources about setting this up would be great.
There are many, many different ways to accomplish this.
Since you're already working with a Google technology, Android - you could start by creating a Google App Engine project. Following the tutorials you can get started setting up a simple back end solution that will store data for you and you can make requests to it for that data.
Another advantage to this for you is that you don't have to learn how to install software on a server and all the dependencies that arise from that, etc. Simply set up a new account and push-button deploy through Eclipse or command line.
And since you've used Java in Android, you can use JAva for Google App Engine (GAE) too!
Getting started: http://code.google.com/appengine/docs/java/gettingstarted/introduction.html
You can try ready to use BAAS/PAAS services to store your data, e.g. QuickBlox for Android http://quickblox.com/developers/Android, where you can manipulate with your data with few strings
QBLocation location = new QBLocation();
location.setLatitude(35.0);
location.setLongitude(53.0);
location.setStatus("I'm at this place");
String someImportantString = "Dr. Henry Walton Indiana Jones";
QBLocations.createLocation(location, new QBCallbackImpl() {
#Override
public void onComplete(Result result, Object context) {
// retrieve context object inside callback
String passedContextObject = (String) context;
System.out.println(passedContextObject);
// do stuff with result
}
}, someImportantString);
All logic of data exchange with server is encapsulated in framework.

Categories

Resources