Expert: Doctor Consult using RxAndroid and MVVM with Huawei Kits (CloudDB/Remote Config) in Android App
Published: 2022-03-18 08:15:10

Overview
In this article, I will create a Doctor Consult Demo App along with the integration of Huawei CloudDB and Remote Config. Which provides an easy interface to consult with doctor. Users can dynamically change the Application configuration using Remote Config.

## 
Cloud DB Service Introduction
Cloud DB is a device-cloud synergy database product that enables seamless data synchronization between the device and cloud and between devices, and supports offline application operations, helping you quickly develop device-cloud and multi-device synergy applications.
Flexible synchronization modes:
Cloud DB supports cache and local data synchronization modes. In cache mode, data on the device is a subset of data on the cloud. If persistent cache is allowed, query results will be automatically cached on the device. In local mode, data is stored locally and is not synchronized to the cloud.
Powerful query capability:
Cloud DB supports various predicate query methods. Multiple chain filtering conditions can be used to filter and sort returned results, and limit the number of objects in the returned result set. In cache mode, data can be queried from the Cloud DB zone on the cloud or that on the local device. In local mode, data is directly queried from the Cloud DB zone on the local device.
Real-time update:
In cache mode of Cloud DB, you can listen on data as needed and use the data synchronization function of Cloud DB to update changed data between the device and cloud in real time.
Offline operations:
In cache mode of Cloud DB, if persistent cache is allowed, the application query is automatically changed from Cloud DB to the local host after the device gets offline. All data modified locally will be automatically synchronized to Cloud DB after the device gets online.
Scalability:
Cloud DB provides powerful HUAWEI CLOUD infrastructure functions, including automatic multi-region data replication, consistency assurance, atomic batch operations, and transaction support.
Security level:
Cloud DB supports device-cloud data full encryption management, triple authentication by app, user, and service, and role-based permission management to ensure data security.

## 
Prerequisite
1.Huawei Phone EMUI 3.0 or later.
2.Non-Huawei phones Android 4.4 or later (API level 19 or higher).
3.Android Studio.
4.AppGallery Account.
App Gallery Integration process
1.Sign In and Create or Choose a project on AppGallery Connect portal.
2.Navigate to Project settings and download the configuration file.
3.Navigate to General Information, and then provide Data Storage location.
4.Navigate to Build then Enable Cloud DB

## 
App Development
1.Create A New Project.
2.Configure Project Gradle.
3.Configure App Gradle.
4.Configure AndroidManifest.xml.
5.Create Activity class with XML UI.
CloudDB:
`package com.hms.doctorconsultdemo.clouddb;

import android.content.Context;
import android.util.Log;

import com.huawei.agconnect.AGCRoutePolicy;
import com.huawei.agconnect.AGConnectInstance;
import com.huawei.agconnect.AGConnectOptionsBuilder;
import com.huawei.agconnect.auth.AGConnectAuth;
import com.huawei.agconnect.cloud.database.AGConnectCloudDB;
import com.huawei.agconnect.cloud.database.CloudDBZone;
import com.huawei.agconnect.cloud.database.CloudDBZoneConfig;
import com.huawei.agconnect.cloud.database.CloudDBZoneQuery;
import com.huawei.agconnect.cloud.database.exceptions.AGConnectCloudDBException;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;

import static android.content.ContentValues.TAG;

public class CloudDB {

    private Context context;
    private static CloudDB instance;

    private AGConnectCloudDB mCloudDB;
    private CloudDBZoneConfig mConfig;
    private CloudDBZone mCloudDBZone;

    private CloudDB(Context context) {
        this.context=context;
    }

    public static CloudDB getInstance(Context context) {
        if (instance==null)instance=new CloudDB(context);
        return instance;
    }

    public CloudDB initAGConnectCloudDB() {
        AGConnectCloudDB.initialize(context);
        return this;
    }

    public CloudDB createCloudDb(){

        AGConnectInstance instance = AGConnectInstance.buildInstance(new AGConnectOptionsBuilder().setRoutePolicy(AGCRoutePolicy.CHINA).build(mContext));
        mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance(instance));
        mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());

        return this;
    }

    public void configCloudDb(){
        mConfig = new CloudDBZoneConfig("QuickStartDemo",
                CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
                CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
        mConfig.setPersistenceEnabled(true);
        Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true);
        openDBZoneTask.addOnSuccessListener(new OnSuccessListener<CloudDBZone>() {
            @Override
            public void onSuccess(CloudDBZone cloudDBZone) {
                Log.i(TAG, "open cloudDBZone success");
                mCloudDBZone = cloudDBZone;
                // Add subscription after opening cloudDBZone success
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                Log.w(TAG, "open cloudDBZone failed for " + e.getMessage());
            }
        });
    }

    public void upsertBookInfos(BookInfo bookInfo) {
        if (mCloudDBZone == null) {
            Log.w(TAG, "CloudDBZone is null, try re-open it");
            return;
        }
        Task<Integer> upsertTask = mCloudDBZone.executeUpsert(bookInfo);
        upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
            @Override
            public void onSuccess(Integer cloudDBZoneResult) {
                Log.i(TAG, "Upsert " + cloudDBZoneResult + " records");
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                mUiCallBack.updateUiOnError("Insert book info failed");
            }
        });
    }

    public void viewCloudDbData(){
        private OnSnapshotListener<BookInfo> mSnapshotListener = new OnSnapshotListener<BookInfo>() {
            @Override
            public void onSnapshot(CloudDBZoneSnapshot<BookInfo> cloudDBZoneSnapshot, AGConnectCloudDBException e) {
                if (e != null) {
                    Log.w(TAG, "onSnapshot: " + e.getMessage());
                    return;
                }
                CloudDBZoneObjectList<BookInfo> snapshotObjects = cloudDBZoneSnapshot.getSnapshotObjects();
                List<BookInfo> bookInfos = new ArrayList<>();
                try {
                    if (snapshotObjects != null) {
                        while (snapshotObjects.hasNext()) {
                            BookInfo bookInfo = snapshotObjects.next();
                            bookInfos.add(bookInfo);
                            updateBookIndex(bookInfo);
                        }
                    }
                    mUiCallBack.onSubscribe(bookInfos);
                } catch (AGConnectCloudDBException snapshotException) {
                    Log.w(TAG, "onSnapshot:(getObject) " + snapshotException.getMessage());
                } finally {
                    cloudDBZoneSnapshot.release();
                }
            }
        };


    }

    public void addSubscription() {
        if (mCloudDBZone == null) {
            Log.w(TAG, "CloudDBZone is null, try re-open it");
            return;
        }

        try {
            CloudDBZoneQuery<BookInfo> snapshotQuery = CloudDBZoneQuery.where(BookInfo.class)
                    .equalTo(BookEditFields.SHADOW_FLAG, true);
            mRegister = mCloudDBZone.subscribeSnapshot(snapshotQuery,
                    CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY, mSnapshotListener);
        } catch (AGConnectCloudDBException e) {
            Log.w(TAG, "subscribeSnapshot: " + e.getMessage());
        }
    }


}
`
## 
App Build Result
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uiyhm2fc4a5ebhs8fvrn.png)
 
## 
Tips and Tricks
Check whether the CloudDBZoneSnapshot object is released. If the CloudDBZoneSnapshot object is not released, the registered listener cannot receive data change notifications. Each time the CloudDBZoneSnapshot object is used, the release() method must be explicitly called to release related resources.
The Cloud DB zone is not closed before being deleted. Ensure that the Cloud DB zone to be deleted is closed. You can delete a Cloud DB zone only after all Cloud DB zone handles are released. Opening a Cloud DB zone must be followed by a closing operation.

## 
Conclusion
In this article, we have learned how to integrate HMS CloudDB and Remote Config in Android application. After completely read this article user can easily implement CloudDB and Remote Config.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.

## 
References
HMS Remote Config Docs: 
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-remoteconfig-introduction-0000001055149778
HMS Cloud DB Docs: 
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-clouddb-introduction-0000001054212760
HMS Training Videos: 
https://developer.huawei.com/consumer/en/training/