Remote Configuration for Android using Alibaba Cloud Table Store

简介: In this tutorial, you will learn how to use Table Store to create a simple user information database with a form accessible on mobile devices.

86e43d940883c108c3166060805b39ee921d58eb_jpeg

By Sai Sarath Chandra Alibaba Cloud Tech Share Author and Alibaba Cloud MVP

In this tutorial, you will learn how to use Table Store to create a simple user information database with a form accessible on mobile devices.

Prerequisites:

• You need an Alibaba Cloud Account. If you need you can get one with $300 by signing up here.
• Need Android Studio 3.0.0
• Basic knowledge of Android/Java.

Tutorial:

1.Activate Table Store Service:

a) Please login to Alibaba Cloud Console and Activate the Table Store service, if you are using this for the first time.

1

b) If you haven't used before you will see a "Activate Now" button. Upon which the service will be activated.
c) Go ahead to the Table Store console and "Create Instance" in the region you are interested in.
d) Fill in the relevant details here.
e) And your Instance should be created. Please click on the Instance and copy the endpoint.
f) This is the Internet endpoint is what we are interested in. Please copy it and keep it for future reference.
g) You can follow the below Illustrative guide for the obtaining access keys.
https://www.alibabacloud.com/help/doc-detail/51665.htm?spm=a3c0i.o51500en.b99.9.733cc1c22ZHdyM

2.Create an Android Project with the name you want

2

a) Select next and I have selected the minimum target Android version as Android Kitkat. Please find the screenshot below.

3

b) Click next and select empty activity

4

c) Click next and leave the defaults as is and select "Finish" button

5

d) Let the Gradle build finish and then follow ahead

3.Downloading dependencies :

a) We need to download 2 jar files which are the core for using the Alibaba Cloud. Please use the following link and download "tablestore-4.2.0-jar-with-dependencies.jar" & "slf4j-api-1.7.7.jar".
http://central.maven.org/maven2/com/aliyun/openservices/tablestore/4.2.0/
http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.7/
b) Add the jars into the libs folder in the projectview and add the jars to the dependencies

6

c) Make sure you right click the jar and the select "Add as library".

4.Adding dependencies to app level gradle.

The following should be your dependencies in gradle.

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile 'joda-time:joda-time:2.9.9'
    compile 'com.android.support:design:26.1.0'
    implementation files('libs/tablestore-4.2.0-jar-with-dependencies.jar')
    implementation files('libs/slf4j-api-1.7.7.jar')
}

Code level Guide:

Project Files:

1.We have three layouts

a) activity_main.xml
b) activity_config.xml
c) activity_welcome.xml

2.we have three activities

a) WelcomeActivity.java
b) RemoteConfig.java
c) MainActivity.java

3.One Strings.xml file, which handles the externalization of strings in the project.

4.One Styles.xml, which externalizes the styles in the project

Deep Dive:

1.First of all, we will talk about creating the layouts of the application. For reference, the following screenshot shows how the final layout will look like:

7

To achieve this create activity_welcome.xml in res > layout & copy the following code.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/alibaba_logo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginBottom="16dp"
        android:layout_marginTop="32dp"
        android:adjustViewBounds="true"
        android:scaleType="centerCrop"
        android:src="@drawable/alibaba_cloud" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/alibaba_logo"
        android:layout_margin="8dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/userForm"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="UserForm" />

        <Button
            android:id="@+id/remoteConfig"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="App Congig" />

    </LinearLayout>
</RelativeLayout>

This is a relative layout which consists of a Image View and Two Button. It is pretty simple layout

2.Create activity_config.xml and copy the following code in the same way what we have did earlier.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/headingText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="Character Count"
        android:textAlignment="center"
        android:textSize="18sp" />

    <android.support.design.widget.TextInputLayout
        android:id="@+id/countLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/headingText"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:textColorHint="@color/colorPrimary"
        app:hintTextAppearance="@style/TextLabel">

        <EditText
            android:id="@+id/countValue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="Character Count"
            android:inputType="text"
            android:textSize="18sp" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/ageMinLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/countLayout"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:textColorHint="@color/colorPrimary"
        app:hintTextAppearance="@style/TextLabel">

        <EditText
            android:id="@+id/ageMinValue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="Minimum Age"
            android:inputType="text"
            android:textSize="18sp" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/ageMaxLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/ageMinLayout"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:textColorHint="@color/colorPrimary"
        app:hintTextAppearance="@style/TextLabel">

        <EditText
            android:id="@+id/ageMaxValue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="Maximum Age"
            android:inputType="text"
            android:textSize="18sp" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/imageLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/ageMaxLayout"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:textColorHint="@color/colorPrimary"
        app:hintTextAppearance="@style/TextLabel">

        <EditText
            android:id="@+id/imageValue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="Image value"
            android:inputType="text"
            android:textSize="18sp" />

    </android.support.design.widget.TextInputLayout>


    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imageLayout"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="16dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="16dp"
        android:text="Update Configuration" />

    <TextView
        android:id="@+id/output"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/button"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:text="Updated Config:"
        android:textAlignment="center"
        android:textColor="#000000"
        android:textSize="18sp" />


</RelativeLayout>

3.The final layout of the file will look something like this:

8

4.Create activity_main.xml and copy the following code. The final layout will look something like this:

9

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="sample.alibabacloud.remoteconfig.MainActivity">

    <ImageView
        android:id="@+id/backgroundImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:alpha="0.7"
        android:scaleType="centerCrop"
        android:src="@drawable/background_2" />
    
    <TextView
        android:id="@+id/headingText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="User Form"
        android:textAlignment="center"
        android:textSize="18sp" />

    <android.support.design.widget.TextInputLayout
        android:id="@+id/nameInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/headingText"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:textColorHint="@color/colorPrimary"
        app:hintTextAppearance="@style/TextLabel">

        <EditText
            android:id="@+id/nameValue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="@string/NameHint"
            android:inputType="text"
            android:textSize="18sp" />

    </android.support.design.widget.TextInputLayout>

    <TextView
        android:id="@+id/ageHeader"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/nameInputLayout"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:text="Age"
        android:textSize="14sp" />

    <Spinner
        android:id="@+id/ageValue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/ageHeader"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:spinnerMode="dialog" />

    <TextView
        android:id="@+id/genderHeader"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/ageValue"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:text="Gender"
        android:textSize="14sp" />


    <RadioGroup
        android:id="@+id/radioGender"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/genderHeader"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp">

        <RadioButton
            android:id="@+id/radioMale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="Male"
            android:textSize="18sp" />

        <RadioButton
            android:id="@+id/radioFemale"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Female"
            android:textSize="18sp" />

    </RadioGroup>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/radioGender"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="16dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="16dp"
        android:text="Submit Form" />

    <TextView
        android:id="@+id/output"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/button"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:text="Click 'SUBMIT FORM' to see output"
        android:textAlignment="center"
        android:textColor="#000000"
        android:textSize="18sp" />

</RelativeLayout>

5.Since we have created all the layouts, we will now work on creating the Activities. Create MainActivity.java file by right clicking on the package > New > Java Class and copy the following code

package sample.alibabacloud.remoteconfig;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    //Creating local objects
    TextInputLayout nameInputLayout;
    EditText nameValue;
    Spinner ageValue;
    String[] ageArr;
    RadioGroup radioGender;
    RadioButton radioMale, radioFemale, selectedRdoBtn;
    Button button;
    TextView output;
    ImageView backgroundImage;

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

        nameInputLayout = findViewById(R.id.nameInputLayout);
        nameValue = findViewById(R.id.nameValue);
        ageValue = findViewById(R.id.ageValue);
        radioGender = findViewById(R.id.radioGender);
        radioMale = findViewById(R.id.radioMale);
        radioFemale = findViewById(R.id.radioFemale);
        button = findViewById(R.id.button);
        output = findViewById(R.id.output);
        backgroundImage = findViewById(R.id.backgroundImage);

        nameInputLayout.setCounterEnabled(true);

        int minAge;
        int maxAge;

        //Logic for checking the App is installed first or existing
        SharedPreferences sharedPreferences = getSharedPreferences(WelcomeActivity.SHARED_PREF_FILE_NAME,MODE_PRIVATE);
        boolean isPresent = sharedPreferences.contains(WelcomeActivity.COUNT_KEY);
        if(isPresent){
            minAge = Integer.parseInt(sharedPreferences.getString(WelcomeActivity.MIN_AGE,null));
            maxAge = Integer.parseInt(sharedPreferences.getString(WelcomeActivity.MAX_AGE,null));
            nameInputLayout.setCounterMaxLength(Integer.parseInt(sharedPreferences.getString(WelcomeActivity.COUNT_KEY,null)));

            if(sharedPreferences.getString(WelcomeActivity.IMG_NUM,null).equals("1")){
                backgroundImage.setImageResource(R.drawable.background_1);
            }else{
                backgroundImage.setImageResource(R.drawable.background_2);
            }

        }else{
            //if the app opening is new
            minAge = 18;
            maxAge = 35;
            nameInputLayout.setCounterMaxLength(20);
        }

        ageArr = new String[maxAge-minAge+1];
        for(int i =minAge;i<=maxAge;i++){
            ageArr[i-minAge] = String.valueOf(i);
        }

        // set the values into the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
        android.R.layout.simple_spinner_item,ageArr);

        ageValue.setAdapter(adapter);
        button.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {

        StringBuffer opStringBuf = new StringBuffer();

        int id = view.getId();
        if(id == R.id.button){

            opStringBuf.append("Form Submitted : ");
            // Get Name Input
            opStringBuf.append("\n Name given : "+nameValue.getEditableText());
            // Get Selected Age
            opStringBuf.append("\n"+"The age selected is : "+ageValue.getSelectedItem());
            // Get selected Gender
            int selectedRadio = radioGender.getCheckedRadioButtonId();
            selectedRdoBtn = findViewById(selectedRadio);
            opStringBuf.append("\n"+"selected Gender : "+selectedRdoBtn.getText());

            output.setText(opStringBuf);

        }

    }

}

6.Create RemoteConfig.java file and copy the following code

package sample.alibabacloud.remoteconfig;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.alicloud.openservices.tablestore.ClientException;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.TableStoreException;
import com.alicloud.openservices.tablestore.model.Column;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import com.alicloud.openservices.tablestore.model.PutRowRequest;
import com.alicloud.openservices.tablestore.model.RowPutChange;
import com.alicloud.openservices.tablestore.model.RowUpdateChange;
import com.alicloud.openservices.tablestore.model.UpdateRowRequest;

import java.util.ArrayList;
import java.util.List;

import sample.alibabacloud.remoteconfig.model.ColumnData;

import static sample.alibabacloud.remoteconfig.WelcomeActivity.TABLE_NAME;

public class RemoteConfig extends Activity implements View.OnClickListener {

    //Fetch class level variables
    Button button;
    EditText countValue,ageMinValue,ageMaxValue,imageValue;
    TextView headingText,output;


    private final static String TAG = "RemoteConfig";

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

        button = findViewById(R.id.button);
        countValue = findViewById(R.id.countValue);
        ageMinValue = findViewById(R.id.ageMinValue);
        ageMaxValue = findViewById(R.id.ageMaxValue);
        imageValue = findViewById(R.id.imageValue);

        headingText = findViewById(R.id.headingText);
        output = findViewById(R.id.output);

        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();

        if(id == R.id.button){
            SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
                    getString(R.string.InstanceName));
            updateConfigValues updateConfigValues = new updateConfigValues();
            updateConfigValues.execute(client);

        }
    }

    private static void updateRow(SyncClient client, ColumnData cData) {
        // Creating Primary Key
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(WelcomeActivity.PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(WelcomeActivity.PRIMARY_KEY_VALUE));
        PrimaryKey primaryKey = primaryKeyBuilder.build();
        // Creating list of Columns
        List<Column> columnsList = new ArrayList<>();

        columnsList.add(new Column(WelcomeActivity.COUNT_KEY,ColumnValue.fromString(cData.getCharCount())));
        columnsList.add(new Column(WelcomeActivity.MIN_AGE,ColumnValue.fromString(cData.getMinAge())));
        columnsList.add(new Column(WelcomeActivity.MAX_AGE,ColumnValue.fromString(cData.getMaxAge())));
        columnsList.add(new Column(WelcomeActivity.IMG_NUM,ColumnValue.fromString(cData.getImageNum())));

        RowUpdateChange rowUpdateChange = new RowUpdateChange(TABLE_NAME, primaryKey);

        rowUpdateChange.put(columnsList);

        try {
            client.updateRow(new UpdateRowRequest(rowUpdateChange));
        } catch (TableStoreException ex) {
            Log.e(TAG, "updateRow: Error "+ex);
            ex.printStackTrace();
        }

        RowPutChange rowPutChange = new RowPutChange(TABLE_NAME, primaryKey);

        rowPutChange.addColumn(WelcomeActivity.COUNT_KEY,ColumnValue.fromString(cData.getCharCount()));
        rowPutChange.addColumn(WelcomeActivity.MIN_AGE,ColumnValue.fromString(cData.getMinAge()));
        rowPutChange.addColumn(WelcomeActivity.MAX_AGE,ColumnValue.fromString(cData.getMaxAge()));
        rowPutChange.addColumn(WelcomeActivity.IMG_NUM,ColumnValue.fromString(cData.getImageNum()));

        client.putRow(new PutRowRequest(rowPutChange));
    }


    class updateConfigValues extends AsyncTask<SyncClient, Void, Void> {

        ProgressDialog loading = new ProgressDialog(RemoteConfig.this);
        SyncClient client;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            loading.setMessage("Updating App Config Values");
            loading.show();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            loading.dismiss();
        }

        @Override
        protected Void doInBackground(SyncClient... syncClients) {
            try {

                client = syncClients[0];
                
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                updateRow( client ,new ColumnData(countValue.getText().toString(),ageMinValue.getText().toString(),ageMaxValue.getText().toString(),imageValue.getText().toString()));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        updateOutput();
                    }
                });

            } catch (TableStoreException e) {
                Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
                Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());

            } catch (ClientException e) {
                Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());

            } finally {
            
            }

            return null;
        }
    }

    private void updateOutput() {
        StringBuffer opBuf = new StringBuffer();

        opBuf.append("Updated Config:\n")
                .append("Character Count : ")
                .append(countValue.getText())
                .append("\nMinimum Age : ")
                .append(ageMinValue.getText())
                .append("\nMaximum Age : ")
                .append(ageMaxValue.getText())
                .append("\nUpdated Image Value : ")
                .append(imageValue.getText());
        
        output.setText(opBuf);
    }
}

7.Create WelcomeActivity.java and copy the following code

package sample.alibabacloud.remoteconfig;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.alicloud.openservices.tablestore.ClientException;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.TableStoreException;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.CreateTableRequest;
import com.alicloud.openservices.tablestore.model.GetRowRequest;
import com.alicloud.openservices.tablestore.model.GetRowResponse;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeySchema;
import com.alicloud.openservices.tablestore.model.PrimaryKeyType;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import com.alicloud.openservices.tablestore.model.PutRowRequest;
import com.alicloud.openservices.tablestore.model.Row;
import com.alicloud.openservices.tablestore.model.RowPutChange;
import com.alicloud.openservices.tablestore.model.SingleRowQueryCriteria;
import com.alicloud.openservices.tablestore.model.TableMeta;
import com.alicloud.openservices.tablestore.model.TableOptions;

import sample.alibabacloud.remoteconfig.model.ColumnData;

/**
 * Created by saisarathchandra on 06/12/17.
 */

public class WelcomeActivity extends Activity implements View.OnClickListener {


    public final static String COUNT_KEY = "CHARACTER_COUNT";
    public final static String MIN_AGE = "MINIMUM_AGE";
    public final static String MAX_AGE = "MAXIMUM_AGE";
    public final static String IMG_NUM = "IMAGE_NUMBER";

    public final static String DEF_COUNT_VALUE = "20";
    public final static String DEF_MIN_AGE = "18";
    public final static String DEF_MAX_AGE = "35";
    public final static String DEF_IMG_NUM = "1";

    private final static String TAG = "WelcomeActivity";
    public final static String SHARED_PREF_FILE_NAME = "RemoteConfigPref";

    public static final String PRIMARY_KEY_NAME = "Version";
    public static final String PRIMARY_KEY_VALUE = "User1";
    public static final String TABLE_NAME = "RemoteConfiguration";

    //Fetch class level variables
    Button userForm,remoteConfig;
    static SharedPreferences sharedPreferences;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);

        userForm = findViewById(R.id.userForm);
        remoteConfig = findViewById(R.id.remoteConfig);

        userForm.setOnClickListener(this);
        remoteConfig.setOnClickListener(this);

        sharedPreferences = getSharedPreferences(SHARED_PREF_FILE_NAME,MODE_PRIVATE);
        boolean isPresent = sharedPreferences.contains(COUNT_KEY);
        if(!isPresent){
            // create Table & Shared preferences and put initial values

            SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
                getString(R.string.InstanceName));

            CreateTableNDefValues createTableNDefValues = new CreateTableNDefValues();
            createTableNDefValues.execute(client);

            SharedPreferences.Editor prefFileEdit = sharedPreferences.edit();

            prefFileEdit.putString(COUNT_KEY,DEF_COUNT_VALUE);
            prefFileEdit.putString(MIN_AGE,DEF_MIN_AGE);
            prefFileEdit.putString(MAX_AGE,DEF_MAX_AGE);
            prefFileEdit.putString(IMG_NUM,DEF_IMG_NUM);

            prefFileEdit.apply();
        }else{
            SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
                    getString(R.string.InstanceName));

            GetRowValues getRowValues = new GetRowValues();
            getRowValues.execute(client);
        }


    }

    @Override
    protected void onResume() {
        super.onResume();
        SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
                getString(R.string.InstanceName));

        GetRowValues getRowValues = new GetRowValues();
        getRowValues.execute(client);
    }

    //Create a Table with a Primary Key Defined with some Additional Parameters
    private static void createTable(SyncClient client) {
        TableMeta tableMeta = new TableMeta(TABLE_NAME);
        tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY_NAME, PrimaryKeyType.STRING));
        int timeToLive = -1;
        int maxVersions = 1;

        TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
        CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions);

        client.createTable(request);
    }

    private static void updatePreferences(Row row){
        
        SharedPreferences.Editor prefFileEdit = sharedPreferences.edit();

        prefFileEdit.putString(COUNT_KEY,row.getColumn(COUNT_KEY).get(0).getValue().toString());
        prefFileEdit.putString(MIN_AGE,row.getColumn(MIN_AGE).get(0).getValue().toString());
        prefFileEdit.putString(MAX_AGE,row.getColumn(MAX_AGE).get(0).getValue().toString());
        prefFileEdit.putString(IMG_NUM,row.getColumn(IMG_NUM).get(0).getValue().toString());

        prefFileEdit.apply();

    }

    private static void putRow(SyncClient client, ColumnData cData) {
        // Creating Primary Key
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(PRIMARY_KEY_VALUE));
        PrimaryKey primaryKey = primaryKeyBuilder.build();

        RowPutChange rowPutChange = new RowPutChange(TABLE_NAME, primaryKey);

//        if you wish to have timestamp, add it as the third parameter to the addColumn method
//        long ts = System.currentTimeMillis();

        rowPutChange.addColumn(COUNT_KEY,ColumnValue.fromString(cData.getCharCount()));
        rowPutChange.addColumn(MIN_AGE,ColumnValue.fromString(cData.getMinAge()));
        rowPutChange.addColumn(MAX_AGE,ColumnValue.fromString(cData.getMaxAge()));
        rowPutChange.addColumn(IMG_NUM,ColumnValue.fromString(cData.getImageNum()));

        client.putRow(new PutRowRequest(rowPutChange));
    }

    private static void getRow(SyncClient client) {
        
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(PRIMARY_KEY_VALUE));
        PrimaryKey primaryKey = primaryKeyBuilder.build();

        
        SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey);
        
        criteria.setMaxVersions(1);
        GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
        Row row = getRowResponse.getRow();
        Log.d(TAG, "getRow: Row Received Compelte");
        Log.d(TAG, "getRow:" + row);


        String[] columnNames = new String[]{WelcomeActivity.COUNT_KEY,
                                            WelcomeActivity.MIN_AGE,
        WelcomeActivity.MAX_AGE,WelcomeActivity.IMG_NUM};

        criteria.addColumnsToGet(columnNames);
        getRowResponse = client.getRow(new GetRowRequest(criteria));
        row = getRowResponse.getRow();

        Log.d(TAG, "getRow: Row Received after get Operation");
        Log.d(TAG, "getRow:" + row);

        updatePreferences(row);
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();

        if(id == R.id.userForm){
            Intent intent = new Intent(this,MainActivity.class);
            startActivity(intent);
        }else if (id == R.id.remoteConfig){
            Intent intent = new Intent(this,RemoteConfig.class);
            startActivity(intent);
        }
    }

    class CreateTableNDefValues extends AsyncTask<SyncClient, Void, Void> {

        ProgressDialog loading = new ProgressDialog(WelcomeActivity.this);
        SyncClient client;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            loading.setMessage("Creating Tables and Setting Values");
            loading.show();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            loading.dismiss();
        }

        @Override
        protected Void doInBackground(SyncClient... syncClients) {
            try {

                client = syncClients[0];

                // Create Table
                createTable(client);

                // Give some time to load
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // Creating Object with all the values
                ColumnData cData = new ColumnData();
                cData.setCharCount(DEF_COUNT_VALUE);
                cData.setMinAge(DEF_MIN_AGE);
                cData.setMaxAge(DEF_MAX_AGE);
                cData.setImageNum(DEF_IMG_NUM);

                // putRow
                putRow(client, cData);

            } catch (TableStoreException e) {
                Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
                Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());

            } catch (ClientException e) {
                Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());

            } finally {
                // Do your maintainence activites here
            }
//            client.shutdown();
            return null;
        }
    }

    class GetRowValues extends AsyncTask<SyncClient, Void, Void> {

        ProgressDialog loading = new ProgressDialog(WelcomeActivity.this);
        SyncClient client;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            loading.setMessage("Fetching updated values");
            loading.show();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            loading.dismiss();
        }

        @Override
        protected Void doInBackground(SyncClient... syncClients) {
            try {

                client = syncClients[0];

                // Give some time to load
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                getRow(client);

            } catch (TableStoreException e) {
                Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
                Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());

            } catch (ClientException e) {
                Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());

            } finally {
                // Do your maintainence activites here
            }

            return null;
        }
    }

}

8.Final configuration files:

• Open your "AndroidManifest.xml"and copy the following code

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="sample.alibabacloud.remoteconfig">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".WelcomeActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity"/>
        <activity android:name=".RemoteConfig"/>
    </application>

</manifest>

• Open your styles.xml under the values folder and copy the following one.

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="TextLabel" parent="TextAppearance.Design.Hint">
        <item name="android:textSize">14sp</item>
    </style>

</resources>

• Update the strings.xml with the values you store earlier

<resources>
    <string name="app_name">RemoteConfig</string>

    <!-- Alibaba TableStore Service details-->
    <!-- Please replace this details with your own-->
    <!--Public Endpoint-->
    <string name="Endpoint"></string>
    <!-- Access ID -->
    <string name="AccessKey"></string>
    <!-- Access key Secret -->
    <string name="AccessKeySecret"></string>
    <!-- Queue Names -->
    <string name="InstanceName">AndroidDemo</string>
    
    <!--App Related Strings-->
    <string name="NameHint">Name</string>

</resources>

Detailed Code Explanation:

  1. In this Remote Configuration project, we are storing some app attributes like the maximum number of characters that the person can enter for his name, minimum age and the maximum age allowed, background image. We can see the user app analytics, review comments in appstore and sometimes we think we need to change some parameters for the code. We tend to launch and update which may not be efficient as we can achieve the same through remote configuration.
  2. When the Code loads the activity_welcome.xml is loaded and the respective WelcomeActivity is initialized. This happens through the WeclomeActivity declaration in the Android manifest.
  3. The welcome activity is responsible for checking the app is installed in the device for the first time or is it installed already. We use this scenario to setup the local secured
    SharedPreferences file.
  4. If the app is opening for the first time we will create a table called "Remote Configuration"& Create a primary key named "Version". And we will update the values to be default. This will be onetime activity along side we will also create the sharedpreferences file and update the values to be default.
  5. We will also copy the same logic onto the onPause() method so whenever the user navigates to the home screen the app can fetch the latest values from the server.
  6. The UserForm screen is responsible for checking the latest values from the TableStore and updating the same in the form.
  7. The RemoteConfig is responsible for updating the values in the TableStore.
  8. You can see all the methods are self-explanatory.
  9. If everything is done correctly all your compilation issues will go away and the application starts installing in by clicking the small play(run) button in the status bar of the android studio.

10

I strongly recommend to clone the repo you will eliminate lot of manual errors and get this app running in minutes.

Please take a look at this repo for the final code repo and let me know if you face any issues or raise any pull requests for improvements.

https://github.com/saichandu415/Table-Store-Android-Sample

相关实践学习
阿里云表格存储使用教程
表格存储(Table Store)是构建在阿里云飞天分布式系统之上的分布式NoSQL数据存储服务,根据99.99%的高可用以及11个9的数据可靠性的标准设计。表格存储通过数据分片和负载均衡技术,实现数据规模与访问并发上的无缝扩展,提供海量结构化数据的存储和实时访问。 产品详情:https://www.aliyun.com/product/ots
目录
相关文章
|
1月前
|
存储 安全 API
请解释Android的推送服务,如Firebase Cloud Messaging(FCM)。
Firebase Cloud Messaging (FCM)是Google的跨平台推送服务,支持Android、iOS和Web,提供实时、高效、安全的消息传递。它利用WebSocket实现低延迟通信,可发送纯文本、富媒体和自定义数据。FCM还支持离线消息存储和安全传输,并提供统计分析功能。要集成FCM,需在Android项目中添加Firebase库和权限设置,通过Firebase API管理消息。
26 0
|
存储 缓存 JSON
Code For Better 谷歌开发者之声——Android 中的 Volley 库
Volley是一个HTTP 库,它使 Android 应用程序的网络变得非常简单和快速。它由 Google 开发并在 2013 年 Google I/O 期间推出。它的开发是因为 Android SDK 中缺少能够在不影响用户体验的情况下工作的网络类。尽管 Volley 是 Android 开源项目 (AOSP) 的一部分,但 Google 在 2017 年 1 月宣布 Volley 将迁移到一个独立的库。它管理网络请求的处理和缓存,并节省开发人员一次又一次编写相同的网络调用/缓存代码的宝贵时间。Volley不适合大型下载或流式操作,因为 Volley 在解析期间将所有响应保存在内存中。
105 0
|
Java Linux Android开发
windows编译FFmpeg for Android 和AndroidStudio使用FFmpeg(二)
FFmpeg的编译是一个大坑,尤其是编译安卓平台的动态库和静态库,应用于APP中。在Linux平台编译是相对简单的,但是我经过尝试在Linux编译静态库没有成功,所以又在windows平台尝试编译了ffempg的动态库,应用成功了,这里分享一下。
314 0
windows编译FFmpeg for Android 和AndroidStudio使用FFmpeg(二)
|
Linux Shell C语言
windows编译FFmpeg for Android 和AndroidStudio使用FFmpeg(一)
FFmpeg的编译是一个大坑,尤其是编译安卓平台的动态库和静态库,应用于APP中。在Linux平台编译是相对简单的,但是我经过尝试在Linux编译静态库没有成功,所以又在windows平台尝试编译了ffempg的动态库,应用成功了,这里分享一下。
470 0
windows编译FFmpeg for Android 和AndroidStudio使用FFmpeg(一)
|
IDE 开发工具 Android开发
解决This Gradle plugin requires a newer IDE able to request IDE model level 3. For Android Studio
解决This Gradle plugin requires a newer IDE able to request IDE model level 3. For Android Studio
131 0
解决This Gradle plugin requires a newer IDE able to request IDE model level 3. For Android Studio
|
开发工具 Android开发
Multiple dex files define Lcom/alibaba/sdk/android/utils/AMSDevReporter$AMSReportStatusEnum;
Multiple dex files define Lcom/alibaba/sdk/android/utils/AMSDevReporter$AMSReportStatusEnum;
121 0
Multiple dex files define Lcom/alibaba/sdk/android/utils/AMSDevReporter$AMSReportStatusEnum;
|
存储 人工智能 Java
TensorFlow Lite for Android 初探(附demo)
TensorFlow Lite for Android 初探(附demo)
450 0
TensorFlow Lite for Android 初探(附demo)
|
开发工具 Android开发
License for package Android SDK Build-Tools 28.0.3 not accepted.
License for package Android SDK Build-Tools 28.0.3 not accepted.
292 0
License for package Android SDK Build-Tools 28.0.3 not accepted.
|
Shell 网络安全 开发工具
【错误记录】Android Studio 向 GitHub 提交代码报错 ( Push failed: Failed with error: Could not read from remote )
【错误记录】Android Studio 向 GitHub 提交代码报错 ( Push failed: Failed with error: Could not read from remote )
180 0
【错误记录】Android Studio 向 GitHub 提交代码报错 ( Push failed: Failed with error: Could not read from remote )
|
开发工具
Could not get unknown property ‘versions‘ for object of type com.android.build.gradle.AppExtension
Could not get unknown property ‘versions‘ for object of type com.android.build.gradle.AppExtension
1623 0
Could not get unknown property ‘versions‘ for object of type com.android.build.gradle.AppExtension