Writing a Serverless Android app (ft. Huawei's AppGallery Connect) - Part 3 - More CloudDB

devwithzachary

Zachary Powell

Posted on August 4, 2021

Writing a Serverless Android app (ft. Huawei's AppGallery Connect) - Part 3 - More CloudDB

Not read part 2? Read it first!

Over the next couple of months I will be releasing a complete end to end guide to creating an Android app using serverless functionality to completely remove the need for any backend server/hosting etc.

At the bottom of this post you will find my twitch channel where we live stream every Thursday at 2pm BST and the GitHub repo for this project!.

But for those that would rather a written guide, lets get into it!

Last time we looked at the basic setup of the CloudDB service, how to get things configured and how you can get started with the service. Now this is in place lets take a look at the service in more detail. We started working on a CloudDBManager that was going to handle all the communication between the CloudDB service and the rest of the app. This was a great start but before we dig any deep lets look at a few improvements to this.

Listen for Database Changes

In the current CloudDBManager if we want to check for any changes to the database we had to manually call getAllUsers(). This is fine if we aren't really interested in when changes are made to the database, however if we do want to keep an up to date local copy of the data (for example posts in a feed) we need to look at adding a Snapshot Listener. This will tell the CloudDB service to execute a given query and process the results in real time.

Lets start by defining a new OnSnapShotListener for the Class User when the listener is given a snapshot we pass this into the processResults method we created last week.

private final OnSnapshotListener<User> snapshotListener = (cloudDBZoneSnapshot, e) -> processResults(cloudDBZoneSnapshot);
Enter fullscreen mode Exit fullscreen mode

Next we create a method that will subscribe that Snapshot Listener to the CloudDBZone with an applied query, in this instance just the simple query to return all Users

public void addSubscription() {
    CloudDBZoneQuery < User > snapshotQuery = CloudDBZoneQuery.where(User.class).equalTo("uid", "");
    try {
        ListenerHandler handler = cloudDBZone.subscribeSnapshot(snapshotQuery,
            CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY,
            snapshotListener);
    } catch (Exception e) {
        toaster.sendErrorToast(context, e.getLocalizedMessage());
    }
}
Enter fullscreen mode Exit fullscreen mode

With this in place we can now tweak the openCloudDBZone method, by using a task based approach we are able to add an onSuccessListener. So long as the zone is successfully opened we can called the addSubscription() method and start listening for new data.

public void openCloudDBZoneV2() {
    CloudDBZoneConfig config = new CloudDBZoneConfig("Barker",
        CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
        CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
    config.setPersistenceEnabled(true);

    Task < CloudDBZone > task = cloudDB.openCloudDBZone2(config, true);
    task.addOnSuccessListener(zone - > {
        cloudDBZone = zone;
        addSubscription();
    }).addOnFailureListener(e - > toaster.sendErrorToast(context, e.getLocalizedMessage()));
}
Enter fullscreen mode Exit fullscreen mode

Use userList

In the processResults method from last week we took the data snapshot and converted it into a list of User objects. We didn't then do anything with this nor did we have a method to pass that data onto somewhere else in the app. Lets change that!

We will start by creating a new public interface called UserCallBack this will be implemented by any class that wants to use an instance of the CloudDBManager. Currently the interface is pretty simple with just three methods:

public interface UserCallBack {
    void onAddOrQuery(List < User > userList);

    void onDelete(List < User > userList);

    void onError(String errorMessage);
}
Enter fullscreen mode Exit fullscreen mode

We create a variable for this Call back within the CloudDBManager like:

private final UserCallBack callBack;
Enter fullscreen mode Exit fullscreen mode

And finally as part of the CloudDBManager init method we require an instance of the UserCallBack

public CloudDBManager(Context context, UserCallBack callBack) {
    cloudDB = AGConnectCloudDB.getInstance();
    this.context = context;
    this.callBack = callBack;
}
Enter fullscreen mode Exit fullscreen mode

Now when we have processed the result of a query we can pass that data back to the calling class using the callback. For example the processResult method now looks like:

private void processResults(CloudDBZoneSnapshot < User > userCloudDBZoneSnapshot) {
    CloudDBZoneObjectList < User > userCursor = userCloudDBZoneSnapshot.getSnapshotObjects();
    List < User > userList = new ArrayList < > ();

    try {
        while (userCursor.hasNext()) {
            User user = userCursor.next();
            updateMaxUserID(user);
            userList.add(user);
        }
        callBack.onAddOrQuery(userList);
    } catch (AGConnectCloudDBException e) {
        callBack.onError(e.getLocalizedMessage());
        toaster.sendErrorToast(context, e.getLocalizedMessage());
    } finally {
        userCloudDBZoneSnapshot.release();
    }
}
Enter fullscreen mode Exit fullscreen mode

We now have a way to both pass back the user list and also any error message that might be given during the processing!

The final CloudDBManager should look something like:

Authentication Manager

With the changes made above lets take a look at how we can use this to gain access to the database data. Specifically within the AuthenticationManager.

First we must now implement the new interface like so:

public class AuthenticationManager implements CloudDBManager.UserCallBack{
...
    @Override
    public void onAddOrQuery(List<User> userList) {

    }

    @Override
    public void onDelete(List<User> userList) {

    }

    @Override
    public void onError(String errorMessage) {

    }
...
}
Enter fullscreen mode Exit fullscreen mode

The onAddOrQuery method will be given the userList returned when a snapshot is processed, we can implement code here to update the UI or confirm if a user is already registered for example.

In this Classes init method we will also need to pass itself in as the UserCallBack. The setup of the CloudDBManager object will now look like:

dbManager = new CloudDBManager(context, this);
dbManager.createObjectType();
dbManager.openCloudDBZoneV2();
Enter fullscreen mode Exit fullscreen mode

We are now in a good position to query data, view it and upsert it! Join us next week when we start configuring the other Objects we are going to be using, expand the User object and make the CloudDBManager generic for any CloudDB Object!

Twitch Account

Github Repo

Barker - A Serverless Twitter Clone

Barker is a simple twitter style social network written to demostrate the serverless services provided by Huawei's AppGallery Connect platform.

This app is written during live streams over at https://www.twitch.tv/devwithzachary The streams are then edited down into videos which you can watch https://www.youtube.com/channel/UC63PqG8ZnWC4JWYrNJKocMA

Finally there is also a written copy of the guide found on dev.to https://dev.to/devwithzachary

Each episodes code is commited as a single commit to make it easy to step along with the code, simply checkout the specific commit and follow along!

We will be back with the next part next week!

💖 💪 🙅 🚩
devwithzachary
Zachary Powell

Posted on August 4, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related