Categories
developer documentation v0.0.27
mimik Developer Documentation
  • Tutorials
  • Creating a Simple Android Application that Uses an edge microservice

Creating a Simple Android Application that Uses an edge microservice

Objective

The objective of this tutorial is to demonstrate how to modify a small Android app to use an edge microservice. The application code will use the mimik Client Library for Android to access an edge microservice that generates a random number on demand.

Intended Readers

The intended readers of this document are software developers who want to familiarize themselves with mimik application development on Android using the mimik Client Library.

In order to get the full benefit of this document, the intended readers should be familiar with the following:

  • The basics of the purpose and use of the mimik mimOE Runtime
  • The basics of using an Access Token to access and work with the mimOE Runtime
  • A basic understanding of programming in Java
  • A basic understanding of Android development using Gradle build tool

What You'll Be Doing

In this tutorial you are going to fix a bug in an existing Android application. The application is a simple Android application that is supposed to generate a random number when a button is clicked. However, there is a problem. The application does not generate a random number as expected, as shown in Figure 1, below.

buggy behavior
Figure 1: The demonstration application does not generate a random number as expected. This needs to be fixed.

Of course you could fix the code by just using the Java Random class to generate the random number directly in the code. However, we're going take another approach. We're going use the broken code as an opportunity to learn how to program Android application code to bind to an edge microservice that provides a random number of demand.

In this tutorial you'll be doing the following tasks to fix the Android application. The tasks below describe by using an edge microservice to generate the random number that the application will display when a button is clicked.

These task are:

  • Clone the demonstration application code from GitHub
  • Configure the demonstration application to include the mimik Client Library for Android as a Maven package
  • Configure the demonstration application with the mimik Client ID and Developer ID Token that will be used to generate the Access Token required to work with the edge microservice running under the mimOE Runtime.
  • Modify the demonstration application by adding code that will do the following:
    • Initialize and start the mimik edgeEngine Client Library
    • Use the mimik Client Library with an existing Developer ID Token to generate the Access Token that is required to deploy an edge microservice via the mimOE Runtime.
    • Deploy an edge microservice
    • Request a random number from the deployed edge microservice

In terms of doing the actual programming, after we've identified the problem area, we'll add the code to the files that will get the mimik Client Library for Android from a private Maven repository. Also, we'll alter the files that store the configure information about the Client ID and Developer ID Token. You'll copy the Client ID and Developer ID Token from the mimik Developer Console.

Then, after the configuration is complete, we'll execute three phases of coding to do the work of actually getting the microservice up and running. The coding will take place in the file MainActivity.java.

In the first phase, we stub out the methods that relate to each programming step. Then, in the second phase, we'll add code to the methods in an isolated manner within the tutorial so the you can learn the reasoning and details about each function. Finally, we'll display the completed file MainActivity.java that has all the code for all the methods. At this point you'll be able to run the fixed code on an Android Device.

Also, be advised that the demonstration application source that you'll clone from GitHub has a branch named completed_code. This branch contains a version of the Android application that has all the code you will be adding throughout the tutorial. You can checkout this branch on your local machine and run that code, should you experience difficulties running the code you've developed.

Technical Prerequisites

This tutorial has the following technical prerequisites:

  • A working Android Studio environment. You can download Android Studio here.
  • An ARM-based Android device. The mimOE Runtime for Android does not support virtual or x86-based devices.

Working with the Demonstration Application and the mimik Client Library

The sections below describe the details of the steps required to fix the broken application code using the mimik Client Library for Android. The mimik Client Library simplifies usage and provides straightforward interfaces to streamline edgeEngine startup, authorization, and edge microservice deployment.

Getting the Source Code

As mentioned above, you'll be modifying an existing Android application to fix a bug in the code. The application you'll modify is an Android Studio project. The application code already has all the UI elements and initialization behavior needed to get the code up and running. The code is operational, but as mentioned, it's buggy.

The place to start is cloning the code from GitHub and loading it into Android Studio.

Step 1: Execute the following command to clone the demonstration code from the GitHub:

git clone https://github.com/mimikgit/random-number-generator-android.git

Step 2: After the source code is cloned from GitHub, open it as an Android Studio project in Android Studio as shown in Figure 2, below.

code in Android Studio
Figure 2: The mimik Random Number Generator application was developed in Android Studio

Once the code is loaded into Android Studio, you're ready to make the modifications necessary to bind to the edge microservice that will provide the code with a random number on demand.

Adding the mimik Client Library for Android to the Application Source Code

As mentioned above, the mimik Client Library for Android, which is a Maven artifact, needs to be made available to the application source code. Also, you'll need another Maven artifact, the OkHttp library to facilitate HTTP requests to the edge microservice you'll be using once it's deployed.

You'll add these references in build.gradle files at both the project and module levels. The listing below shows the location of the relevant build.gradle files within the application project's file system. Notice that the location of the project level build.gradle file is at Line 9. Also, the location of the module level build.gradle file is at Line 3.

1: .
2: ├── app
3: │   ├── build.gradle //module level
4: │   ├── proguard-rules.pro
5: │   └── src
6: │   ├── androidTest
7: │   ├── main
8: │   └── test
9: ├── build.gradle //project level
10: ├── gradle
11: │   └── wrapper
12: │   ├── gradle-wrapper.jar
13: │   └── gradle-wrapper.properties
14: ├── gradle.properties
15: ├── gradlew
16: ├── gradlew.bat
17: ├── LICENSE
18: ├── README.md
19: └── settings.gradle

Keep the locations of the project level and module level build.gradle files in mind as you do the following steps.

Step 1: From within Android Studio, add the code shown below at Lines 13-15 to the project level settings.gradle file.

1: pluginManagement {
2: repositories {
3: google()
4: mavenCentral()
5: gradlePluginPortal()
6: }
7: }
8: dependencyResolutionManagement {
9: repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10: repositories {
11: google()
12: mavenCentral()
13: maven {
14: url "https://s3-us-west-2.amazonaws.com/mimik-android-repo"
15: }
16: }
17: }
18: rootProject.name = "mimik Random Number Generator"
19: include ':app'

The code you add is a reference to the private Maven repository in which the mimik Client Library for Android is hosted. Configuring the reference makes the library available to the demonstration application source code.

Step 2: Add the code shown below at Lines 42 - 43 to the application's module level build.gradle.

1: plugins {
2: id 'com.android.application'
3: }
4:
5: android {
6: namespace 'com.mimik.randomnumbergen'
7: compileSdk 33
8:
9: defaultConfig {
10: applicationId "com.mimik.randomnumbergen"
11: minSdkVersion 26
12: targetSdkVersion 33
13: versionCode 1
14: versionName "1.0"
15:
16: testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17:
18: manifestPlaceholders = ['appAuthRedirectScheme': ''] // Placeholder, for use with CustomTab authentication methods
19: }
20:
21: buildTypes {
22: release {
23: minifyEnabled false
24: proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25: }
26: }
27: compileOptions {
28: sourceCompatibility JavaVersion.VERSION_17
29: targetCompatibility JavaVersion.VERSION_17
30: }
31: }
32:
33: dependencies {
34: implementation 'androidx.appcompat:appcompat:1.6.1'
35: implementation 'com.google.android.material:material:1.9.0'
36: implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
37:
38: testImplementation 'junit:junit:4.13.2'
39: androidTestImplementation 'androidx.test.ext:junit:1.1.5'
40: androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
41:
42: implementation 'com.mimik.edgesdk-android-client:edgemobileclient-core:0.3.8.3'
43: implementation "com.squareup.okhttp3:okhttp:4.9.3"
44: }

The code you added at Lines 42 - 43 declares the edgemobileclient and okhttp packages as dependencies within the application.

At this point you're going to need to get the Client ID and Developer ID Token from your mimik Project within the mimik Developer Console as shown in Figure 3 below.

NOTE:
The Developer ID Token is labeled as ID Token in the mimik Developer Console UI.
Client ID and Developer ID Token
Figure 3: The Client ID and Developer ID Token are stored in the mimik Developer Console

Step 3: Copy the Client ID and Developer ID Token from the mimik Developer Console and then paste the values into a place where you can use them later in the Step 5.

Step 4: Insert the code shown in the listing below at Lines 19 - 21 into the module-level build.gradle file at app/build.gradle, as well as Lines 34 - 36.

Please notice there are placeholders <YOUR_CLIENT_ID> and <YOUR_DEVELOPER_ID_TOKEN> in the code at Lines 20 - 21. You'll substitute the actual values for the placeholders in the next step. (You can read the details about the mimik Client ID and Developer ID Token in the Key Concepts page here.)

Lines 34 - 36 enable usage of the configuration values specified in Lines 19 - 21.

1: plugins {
2: id 'com.android.application'
3: }
4:
5: android {
6: namespace 'com.mimik.randomnumbergen'
7: compileSdk 33
8:
9: defaultConfig {
10: applicationId "com.mimik.randomnumbergen"
11: minSdkVersion 26
12: targetSdkVersion 33
13: versionCode 1
14: versionName "1.0"
15:
16: testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17:
18: manifestPlaceholders = ['appAuthRedirectScheme': ''] // Placeholder, for use with CustomTab authentication methods
19: buildConfigField "String", "AUTHORIZATION_ENDPOINT", '"https://devconsole-mid.mimik.com"'
20: buildConfigField "String", "CLIENT_ID", '"<YOUR_CLIENT_ID>"'
21: buildConfigField "String", "DEVELOPER_ID_TOKEN", '"<YOUR_DEVELOPER_ID_TOKEN>"'
22: }
23:
24: buildTypes {
25: release {
26: minifyEnabled false
27: proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28: }
29: }
30: compileOptions {
31: sourceCompatibility JavaVersion.VERSION_17
32: targetCompatibility JavaVersion.VERSION_17
33: }
34: buildFeatures {
35: buildConfig true
36: }
37: }
38:
39: dependencies {
40: implementation 'androidx.appcompat:appcompat:1.6.1'
41: implementation 'com.google.android.material:material:1.9.0'
42: implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
43:
44: testImplementation 'junit:junit:4.13.2'
45: androidTestImplementation 'androidx.test.ext:junit:1.1.5'
46: androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
47:
48: implementation 'com.mimik.edgesdk-android-client:edgemobileclient-core-developer:0.3.10.0'
49: implementation "com.squareup.okhttp3:okhttp:4.12.0"
50: }

The entries you make in the listing above at Lines 19 - 21 provide the application with important information that the code needs in order to interact with the functionality within the edgeCloud.

Step 5: Go back to the module-level build.gradle file and substitute the Client ID and Developer ID Token values copied from the mimik Developer Console at the location of the <YOUR_CLIENT_ID> and <YOUR_DEVELOPER_ID_TOKEN> placeholders at Line 20 and Line 21. The result will be similar to the following, but your values will be special according those stored in your mimik Developer Portal project.

buildConfigField "String", "CLIENT_ID", '"394a7d43-10b2-4acd-94b7-516929b652e7"'
buildConfigField "String", "DEVELOPER_ID_TOKEN", '"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjV2RG45aF9LSHNxTkJZNVZubUxFd0VfUVNpQTkzXXXXXXXXXXVRdVl5T2sifQ.eyJzdWIiOiIyNDM2MTY2NTQxMDQ4ODU5NzA4IiwvvvvvvvvvvOiIyNDM2MTY2NTQxMDQ4ODU5NzA4QGV4YW1wbGUuY29tIiwiYXVkIjoiMzk0YTdkNDMtMTBiMi00YWNkLTk0YjctNTE2OTI5YjY1MmU3IiwiZXhwIjoxNjM0MjkwMjE0LCJpYXQiOjE2MzE2OTgyMTQsImlzcyI6Imh0dHBzOi8vbWlkLm1pbWlrMzYwLmNvbSJ9.evIoeZyodbjhncobaa3NiAmOv0Wb_B_tedib_7HgYkpppppppppp6IqxS7ysKttC8hdZJVGdTbP1bMNT4YrIGtxiL5UzUpsuktnRXoOxQGI9P8N5d5uw7fjqNPZlGDb_wkOhRkR3g5bp8LlVlayA_xqkkCjoSV8cGgrlUfXbW6COTnZvHtPfaiIzHxPY1RMZRSYytFwMdS6vGe80OqBnnAlt6b-it8v3UEr8Nj-Dv94HtArAeFWPqcsISztH5VbRkCHzslqLVR2PP0FfA87yMS3cCBwcUofcincEvfuNRPxK9XHIA203Vwxx24zOv8HtlMqTsW4uxjkYeJNwTUXvcQ"'

The application now has the references it needs to include the mimik Client Library for Android and the information token values it needs to work with the library. The last step in the configuration process is to enable cleartext communication to and from the edge microservice.

Step 6: To enable cleartext communication add the line of text android:usesCleartextTraffic="true" at Line 6 in the AndroidManifest.xml as shown in the listing below. The XML file is located at app/src/main/AndroidManifest.xml in the working directory of the demonstration application.

1: <?xml version="1.0" encoding="utf-8"?>
2: <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3: package="com.mimik.randomnumbergen">
4:
5: <application
6: android:usesCleartextTraffic="true"
7: android:allowBackup="true"
8: android:icon="@mipmap/ic_launcher"
9: android:label="@string/app_name"
10: android:roundIcon="@mipmap/ic_launcher_round"
11: android:supportsRtl="true"
12: android:theme="@style/Theme.randomnumbergen">
13: <activity android:name=".MainActivity"
14: android:exported="true">
15: <intent-filter>
16: <action android:name="android.intent.action.MAIN" />
17:
18: <category android:name="android.intent.category.LAUNCHER" />
19: </intent-filter>
20: </activity>
21: </application>
22: </manifest>

Now that references and configurations have been set, it's time to get to the business of programming to the edge microservice.

Identifying the Bug

As mentioned at the beginning of this tutorial, our objective is to fix a bug that is preventing the demonstration application from displaying a random number when a button is clicked on the screen of the cell phone. The bad behavior we need to fix as in the MainActivity.java file located at app/src/main/java/com/mimik/randomnumbergen/MainActivity.java. The listing below shows the faulty code. The error is at Lines 21 - 24.

1: package com.mimik.randomnumbergen;
2:
3: import android.os.Bundle;
4: import android.view.View;
5: import android.widget.Toast;
6:
7: import androidx.appcompat.app.AppCompatActivity;
8:
9: import java.util.Random;
10:
11: public class MainActivity extends AppCompatActivity {
12:
13: @Override
14: protected void onCreate(Bundle savedInstanceState) {
15: super.onCreate(savedInstanceState);
16: setContentView(R.layout.activity_main);
17:
18: findViewById(R.id.btn_get).setOnClickListener(this::onGetClicked);
19: }
20:
21: private void onGetClicked(View view) {
22: Random random = new Random(0);
23: Toast.makeText(this, "Got " + random.nextInt(100), Toast.LENGTH_LONG).show();
24: }
25: }

Notice that code uses a seed value of 0 in the expression Random random = new Random(0); at Line 22. Operationally this disables randomization. This is the root cause. The work we'll do moving forward will remedy this issue. We're going to make the fix by using an edge microservice as we discussed earlier.

Implementing the Fix

The first task we'll need to do is to create an instance of the mimik Client Library for Android. The mimik Client Library for Android is represented in code as the class EdgeMobileClient.

Step 1: Insert the Java import statements for the classes in the com.mimik.edgemobileclient and okhttp3 namespaces into the file app/src/main/java/com/mimik/randomnumbergen/MainActivity.java as shown below at Lines 10 - 24.

Also, insert the import statement for a required class org.jetbrains.annotations.NotNull at Line 26 and insert two import statements for some additional Java classes as shown at Lines 28 - 29. Finally, insert the import statement for the class android.net.Uri at Line 3 as shown in the listing below.

Finally, insert the class variables for we'll be using in the class MainActivity as shown at Lines 35 - 37.

1: package com.mimik.randomnumbergen;
2:
3: import android.net.Uri;
4: import android.os.Bundle;
5: import android.view.View;
6: import android.widget.Toast;
7:
8: import androidx.appcompat.app.AppCompatActivity;
9:
10: import com.mimik.edgemobileclient.EdgeMobileClient;
11: import com.mimik.edgemobileclient.EdgeRequestError;
12: import com.mimik.edgemobileclient.EdgeRequestResponse;
13: import com.mimik.edgemobileclient.EdgeResponseHandler;
14: import com.mimik.edgemobileclient.authobject.CombinedAuthResponse;
15: import com.mimik.edgemobileclient.authobject.DeveloperTokenLoginConfig;
16: import com.mimik.edgemobileclient.edgeservice.EdgeConfig;
17: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentConfig;
18: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentStatus;
19:
20: import okhttp3.Call;
21: import okhttp3.Callback;
22: import okhttp3.OkHttpClient;
23: import okhttp3.Request;
24: import okhttp3.Response;
25:
26: import org.jetbrains.annotations.NotNull;
27:
28: import java.io.IOException;
29: import java.util.concurrent.Executors;
30:
31: import java.util.Random;
32:
33: public class MainActivity extends AppCompatActivity {
34:
35: EdgeMobileClient edgeMobileClient;
36: String accessToken;
37: String randomNumberRoot;
38:
39: @Override
40: protected void onCreate(Bundle savedInstanceState) {
41: super.onCreate(savedInstanceState);
42: setContentView(R.layout.activity_main);
43:
44: findViewById(R.id.btn_get).setOnClickListener(this::onGetClicked);
45: }
46:
47: private void onGetClicked(View view) {
48: Random random = new Random(0);
49: Toast.makeText(this, "Got " + random.nextInt(100), Toast.LENGTH_LONG).show();
50: }
51: }

At this point all the configuration activities have been completed in the relevant files. Also, the required import statements and class variables have been added to file MainActivity.java. Now it's time to do some detailed programming in the file MainActivity.java.

Inserting the Method Placeholders

In order to get the edge microservice installed and accessible we'll need to do the following in the file MainActivity.java:

  • modify the method onCreate()
  • modify the method onGetClicked(View view)
  • insert the private class method startEdge()
  • insert the private class method authorizeEdge()
  • insert the private class method authorizeEdge()
  • insert the private class method deployRandomNumberMicroservice()

The code in the listing below is a repeat of the file MainActivity.java shown above. However, this version of the code shown below has all the programming statements code removed from the methods onCreate(Bundle savedInstanceState) and onGetClicked(View view) at Lines 35 - 44. Also, the listing has the placeholder methods startEdge(), authorizeEdge() and deployRandomNumberMicroservice() inserted at Lines 45 - 60.

Take a look at the listing. Notice that the inserted methods have comments that describe their purpose.

1: package com.mimik.randomnumbergen;
2:
3: import android.net.Uri;
4: import android.os.Bundle;
5: import android.view.View;
6: import android.widget.Toast;
7:
8: import androidx.appcompat.app.AppCompatActivity;
9:
10: import com.mimik.edgemobileclient.EdgeMobileClient;
11: import com.mimik.edgemobileclient.EdgeRequestError;
12: import com.mimik.edgemobileclient.EdgeRequestResponse;
13: import com.mimik.edgemobileclient.EdgeResponseHandler;
14: import com.mimik.edgemobileclient.authobject.CombinedAuthResponse;
15: import com.mimik.edgemobileclient.authobject.DeveloperTokenLoginConfig;
16: import com.mimik.edgemobileclient.edgeservice.EdgeConfig;
17: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentConfig;
18: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentStatus;
19:
20: import okhttp3.Call;
21: import okhttp3.Callback;
22: import okhttp3.OkHttpClient;
23: import okhttp3.Request;
24: import okhttp3.Response;
25:
26: import org.jetbrains.annotations.NotNull;
27:
28: import java.io.IOException;
29: import java.util.concurrent.Executors;
30:
31: import java.util.Random;
32:
33: public class MainActivity extends AppCompatActivity {
34: // Called when the application is created
35: @Override
36: protected void onCreate(Bundle savedInstanceState) {
37:
38: }
39:
40: // Called with the Get Random Number is clicked
41: private void onGetClicked(View view) {
42:
43: }
44:
45: // The code that will get and start the mimOE Runtime
46: private void startEdge() {
47:
48: }
49:
50: // The code that will get the Access Token that's necessary to work
51: // with the edge microservice running under the mimOE Runtime
52: private void authorizeEdge() {
53:
54: }
55:
56: // The code that will deploy the edge microservice under the
57: // mimOE Runtime
58: private void deployRandomNumberMicroservice() {
59:
60: }
61: }

The sections that follow will show the code for each method we're programming. Also, we'll describe the reasoning behind each of the additions we're making to the code in the file MainActivity.java.

Refactoring onCreate()

In order to use the mimik Client Library for Android, we need to create an instance of it running within the application. We'll do this by initializing the class variable edgeMobileClient from within the function onCreate().

Initialization of mimOE Runtime using the mimik Client Library automatically selects default parameters. This will have the mimOE Runtime host URLs using port 8083, and use the default license required to work with the mimOE Runtime.

The listing below shows the code that executes this task. Notice that the instance variable is initialized at Line 6.

1: protected void onCreate(Bundle savedInstanceState) {
2: super.onCreate(savedInstanceState);
3: setContentView(R.layout.activity_main);
4:
5: // Instantiate new instance of mimOE Runtime
6: edgeMobileClient = new EdgeMobileClient(this, new EdgeConfig());
7:
8: // Start edge using a new thread so as not to slow down activity creation
9: Executors.newSingleThreadExecutor().execute(this::startEdge);
10:
11: findViewById(R.id.btn_get).setOnClickListener(this::onGetClicked);
12: }

Notice that a Line 9 in the code above, we're starting the mimOE Runtime in new thread by calling the method startEdge(). The details of this method are discussed next.

Programming startEdge()

Once the class variable edgeMobileClient has been initialized, we can use it to get the mimOE Runtime up and running. The listing below shows the method startEdge(). The method has the code that will initialize and start the mimOE Runtime.

Notice that edgeMobileClient is initialized in Line 2 using the client library method edgeMobileClient.startEdge().

1: private void startEdge() {
2: if (edgeMobileClient.startEdge()) { // Start mimOE Runtime
3: runOnUiThread(() -> {
4: Toast.makeText(
5: MainActivity.this,
6: "edgeEngine started!",
7: Toast.LENGTH_LONG).show();
8: });
9: try {
10: Thread.sleep(1000); // Wait for edge to finish startup
11: } catch (InterruptedException e) {
12: e.printStackTrace();
13: }
14: authorizeEdge();
15: } else {
16: runOnUiThread(() -> {
17: Toast.makeText(
18: MainActivity.this,
19: "edgeEngine failed to start!",
20: Toast.LENGTH_LONG).show();
21: });
22: }
23: }

Notice at Line 14 in the code above that there is a call to the method authorizeEdge(). This is the method that does the work of getting an Access Token to use to deploy the edge microservice. The details of authorizeEdge() are discussed in the next section.

Programming authorizeEdge()

Once the mimOE Runtime is initialized and running the class-level instance variable edgeMobileClient, we need to apply some configuration settings to a DeveloperTokenLoginConfig object in order to eventually get an Access Token. The configuration settings we'll apply are the Client ID and the Developer ID Token copied earlier from the mimik Developer Console and stored in the Gradle BuildConfig file. Take a look the listing below. It's heavily commented to describe the intention of the particular lines of code.

1: private void authorizeEdge() {
2: // Get the DEVELOPER_ID_TOKEN from the BuildConfig settings
3: String developerIdToken = BuildConfig.DEVELOPER_ID_TOKEN;
4:
5: String clientId = BuildConfig.CLIENT_ID; // The Client ID
6:
7: // Create mimik configuration object for Developer ID Token login
8: DeveloperTokenLoginConfig config = new DeveloperTokenLoginConfig();
9:
10: // Set the root URL
11: config.setAuthorizationRootUri(BuildConfig.AUTHORIZATION_ENDPOINT);
12:
13: // Set the value for the DEVELOPER_ID_TOKEN
14: config.setDeveloperToken(developerIdToken);
15:
16: // Set the value for the CLIENT_ID
17: config.setClientId(clientId);
18:
19: // Login to the edgeCloud
20: edgeMobileClient.loginWithDeveloperToken(
21: this,
22: config,
23: new EdgeResponseHandler() {
24: @Override
25: public void onError(EdgeRequestError edgeRequestError) {
26: // Display error message
27: runOnUiThread(() -> {
28: Toast.makeText(
29: MainActivity.this,
30: "Error getting access token! " + edgeRequestError.getErrorMessage(),
31: Toast.LENGTH_LONG).show();
32: });
33: }
34:
35: // A valid return makes the Access Token available by way of
36: // the method, edgeMobileClient.getCombinedAccessTokens()
37: @Override
38: public void onResponse(EdgeRequestResponse edgeRequestResponse) {
39:
40: // Get all the token that are stored within the
41: // edgeMobileClient
42: CombinedAuthResponse tokens = edgeMobileClient.getCombinedAccessTokens();
43:
44: // Extract the Access Token from the tokens object and assign
45: // it to the class variable, accessToken
46: accessToken = tokens.getMimikTokens().getAccessToken();
47: runOnUiThread(() -> {
48: Toast.makeText(
49: MainActivity.this,
50: "Got access token!",
51: Toast.LENGTH_LONG).show();
52: });
53:
54: // Deploy edge microservice now that an access token
55: // has been generated
56: deployRandomNumberMicroservice();
57: }
58: }
59: );
60: }

Notice the statement at Line 56 in the listing above. That code calls deployRandomNumberMicroservice(). As mentioned in our earlier description, deployRandomNumberMicroservice() is the method that does the work of deploying the edge microservice of interest to run in the mimOE Runtime. The details of working with deployRandomNumberMicroservice() are described in the section that follows.

Programming deployRandomNumberMicroservice()

After an Access Token has been generated in the method authorizeEdge() as shown above, we're ready to deploy the microservice that will return a random number. The microservice is represented as the file randomnumber_v1.tar. That file is stored in the directory app/src/main/res/raw/ . You can think of the file randomnumber_v1.tar as the Image for the microservice. The microservice will be deployed as an edge Container created from the edge Image.

The code below shows the method deployRandomNumberMicroservice() with all the code that needed to deploy and run the microservice that generates a random number. We've taken the liberty of configuring the MicroserviceDeploymentConfig using hardcoded values. This is done to make the code easier to understand. In a production setting you'll probably put all the hard coded values in the module level build.gradle configuration file and naming the values accordingly.

The code below is heavily commented. Take a moment to review statements using the code comments as your guide.

1: private void deployRandomNumberMicroservice() {
2:
3: // Create microservice deployment configuration, dependent
4: // on microservice implementation
5: MicroserviceDeploymentConfig config = new MicroserviceDeploymentConfig();
6:
7: // set the name that will represent the microservice
8: config.setName("randomnumber-v1");
9:
10: // Get the tar file that represents the edge microservice
11: //but stored in the project's file system as a Gradle resource
12: config.setResourceStream(getResources().openRawResource(R.raw.randomnumber_v1));
13:
14: // Set the filename that by which the edge client will identify
15: // the microservice internally. This filename is associated internally
16: // with the resource stream initialized above
17: config.setFilename("randomnumber_v1.tar");
18:
19: // Declare the URI by which the application code will access
20: // the microservice
21: config.setApiRootUri(Uri.parse("/randomnumber/v1"));
22:
23: // Deploy edge microservice using the client library instance variable
24: MicroserviceDeploymentStatus status =
25: edgeMobileClient.deployEdgeMicroservice(accessToken, config);
26: if (status.error != null) {
27: // Display microservice deployment error
28: runOnUiThread(() -> {
29: Toast.makeText(
30: MainActivity.this,
31: "Failed to deploy microservice! " + status.error.getMessage(),
32: Toast.LENGTH_LONG).show();
33: });
34: } else {
35: // Store the microservice API root URI in the class variable,
36: // randomNumberRoot
37: randomNumberRoot = status.response.getContainer().getApiRootUri().toString();
38: // Display a message indicating a successful microservice deployment
39: runOnUiThread(() -> {
40: Toast.makeText(
41: MainActivity.this,
42: "Successfully deployed microservice!",
43: Toast.LENGTH_LONG).show();
44: });
45: }
46: }

At this point all the code that is required to make it so the application deploys the microservice is in place. The next step is to call the microservice to actually get a random number. This is done in the following section that shows you the code to execute when the button on the cell phone screen gets clicked. The details of this method are discussed next.

Refactoring onGetClicked(View view)

The code below is a refactoring of the method onGetClicked(View view). This is the behavior that executes when someone click the Get Random Number on the cell phone screen. The code is divided into two parts.

First at Lines 4 - 10 the code builds a the URL that the OkHttpClient HTTP client object will use to retrieve the random number from the microservice.

Then, at Line 11 the actual HTTP request is executed. The code between Lines 12 - 52 to the actual work of processing the response (or error) from the HTTP request. If all is well, Lines 40 - 45 do the work of extracting the random number from the response from the microservice. The random number is then displayed in the cell phone screen, within the text, Got ####, WHERE #### is the random number.

1: private void onGetClicked(View view) {
2: // Construct an API request for the edge microservice
3: OkHttpClient client = new OkHttpClient();
4: Request request = new Request.Builder()
5: .url(String.format(
6: "http://127.0.0.1:%d%s/randomNumber",
7: // use the client to get the default localhost port
8: edgeMobileClient.getEdgePort(),
9: randomNumberRoot)) // root URI determined by microservice deployment
10: .build();
11: client.newCall(request).enqueue(new Callback() {
12: @Override
13: public void onFailure(
14: @NotNull Call call,
15: @NotNull IOException e) {
16: // Display microservice request error
17: e.printStackTrace();
18: runOnUiThread(() -> {
19: Toast.makeText(
20: MainActivity.this,
21: "Failed to communicate with microservice! " + e.getMessage(),
22: Toast.LENGTH_LONG).show();
23: });
24: }
25:
26: @Override
27: public void onResponse(
28: @NotNull Call call,
29: @NotNull final Response response) throws IOException {
30: if (!response.isSuccessful()) {
31: // Display microservice unknown error
32: runOnUiThread(() -> {
33: Toast.makeText(
34: MainActivity.this,
35: "Microservice returned unexpected code! " + response,
36: Toast.LENGTH_LONG).show();
37: });
38: } else {
39: // Display microservice response
40: runOnUiThread(() -> {
41: try {
42: Toast.makeText(
43: MainActivity.this,
44: "Got " + response.body().string(),
45: Toast.LENGTH_LONG).show();
46: } catch (IOException e) {
47: e.printStackTrace();
48: }
49: });
50: }
51: }
52: });
53: }

We now have fully operational code. But, there is one last topic to cover: how to reset the mimOE Runtime, which we'll cover next.

Resetting the mimOE Runtime

At this point we've added all the code necessary to fix the original bug in the demonstration application. But, there is still an outstanding risk. We really haven't programmed a way to reset the state of the underlying mimOE Runtime that is supporting the microservice. Let's cover this topic now, briefly.

Resetting the mimOE Runtime will remove all authorization data and reset the authorization status. Resetting the mimOE Runtime also clears any data stored and and removes the deployed microservices. Finally, resetting stops the mimOE Runtime. The mimOE Runtime will return to an unauthorized, pristine state.

If you want to add reset the behavior to the demonstration application, you can use the following code to accomplish this task.

// Stop edgeEngine and clear authorization, state, and stored data
Executors.newSingleThreadExecutor().execute(() -> edgeMobileClient.clearEdgeData());

You'll need to figure out the best place to put the code. One way to implement the behavior is to add another button to the application's screen and put the reset behavior in that button's click handler.

No matter how you decide to implement reset behavior, the important thing to remember is that the mimik Client Library for Android provides the capability to reset the state of the underlying mimOE Runtime and that reset behavior is executed using the methods edgeMobileClient.clearEdgeData().

Viewing the Completed Code for MainActivity.java

The sections above showed you the details that go with getting the configuration settings, import statements, class variables and methods in place in order to implement an edge microservice. The microservice we added fixes the bug that was in the demonstration application. The listing below shows the entirety of the file MainActivity.java with all the code added to the refactored and placeholders methods.

1: package com.mimik.randomnumbergen;
2: import android.net.Uri;
3: import android.os.Bundle;
4: import android.view.View;
5: import android.widget.Toast;
6:
7: import androidx.appcompat.app.AppCompatActivity;
8:
9: import com.mimik.edgemobileclient.EdgeMobileClient;
10: import com.mimik.edgemobileclient.EdgeRequestError;
11: import com.mimik.edgemobileclient.EdgeRequestResponse;
12: import com.mimik.edgemobileclient.EdgeResponseHandler;
13: import com.mimik.edgemobileclient.authobject.CombinedAuthResponse;
14: import com.mimik.edgemobileclient.authobject.DeveloperTokenLoginConfig;
15: import com.mimik.edgemobileclient.edgeservice.EdgeConfig;
16: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentConfig;
17: import com.mimik.edgemobileclient.microserviceobjects.MicroserviceDeploymentStatus;
18:
19: import okhttp3.Call;
20: import okhttp3.Callback;
21: import okhttp3.OkHttpClient;
22: import okhttp3.Request;
23: import okhttp3.Response;
24:
25: import org.jetbrains.annotations.NotNull;
26:
27: import java.io.IOException;
28: import java.util.concurrent.Executors;
29:
30:
31: import java.util.Random;
32:
33: public class MainActivity extends AppCompatActivity {
34: EdgeMobileClient edgeMobileClient;
35: String accessToken;
36: String randomNumberRoot;
37:
38: @Override
39: protected void onCreate(Bundle savedInstanceState) {
40: super.onCreate(savedInstanceState);
41: setContentView(R.layout.activity_main);
42:
43: // Instantiate new instance of mimOE Runtime
44: edgeMobileClient = new EdgeMobileClient(this, new EdgeConfig());
45:
46: // Start edge using a new thread so as not to slow down activity creation
47: Executors.newSingleThreadExecutor().execute(this::startEdge);
48:
49: findViewById(R.id.btn_get).setOnClickListener(this::onGetClicked);
50: }
51:
52: private void startEdge() {
53: if (edgeMobileClient.startEdge()) { // Start mimOE Runtime
54: runOnUiThread(() -> {
55: Toast.makeText(
56: MainActivity.this,
57: "edgeEngine started!",
58: Toast.LENGTH_LONG).show();
59: });
60: try {
61: Thread.sleep(1000); // Wait for edge to finish startup
62: } catch (InterruptedException e) {
63: e.printStackTrace();
64: }
65: authorizeEdge();
66: } else {
67: runOnUiThread(() -> {
68: Toast.makeText(
69: MainActivity.this,
70: "edgeEngine failed to start!",
71: Toast.LENGTH_LONG).show();
72: });
73: }
74: }
75:
76: private void authorizeEdge() {
77: // Get the DEVELOPER_ID_TOKEN from the BuildConfig settings
78: String developerIdToken = BuildConfig.DEVELOPER_ID_TOKEN;
79:
80: String clientId = BuildConfig.CLIENT_ID; // The Client ID
81:
82: // Create mimik configuration object for Developer ID Token login
83: DeveloperTokenLoginConfig config = new DeveloperTokenLoginConfig();
84:
85: // Set the root URL
86: config.setAuthorizationRootUri(BuildConfig.AUTHORIZATION_ENDPOINT);
87:
88: // Set the value for the DEVELOPER_ID_TOKEN
89: config.setDeveloperToken(developerIdToken);
90:
91: // Set the value for the CLIENT_ID
92: config.setClientId(clientId);
93:
94: // Login to the edgeCloud
95: edgeMobileClient.loginWithDeveloperToken(
96: this,
97: config,
98: new EdgeResponseHandler() {
99: @Override
100: public void onError(EdgeRequestError edgeRequestError) {
101: // Display error message
102: runOnUiThread(() -> {
103: Toast.makeText(
104: MainActivity.this,
105: "Error getting access token! " + edgeRequestError.getErrorMessage(),
106: Toast.LENGTH_LONG).show();
107: });
108: }
109:
110: // A valid return makes the Access Token available by way of
111: // the method, edgeMobileClient.getCombinedAccessTokens()
112: @Override
113: public void onResponse(EdgeRequestResponse edgeRequestResponse) {
114:
115: // Get all the token that are stored within the
116: // edgeMobileClient
117: CombinedAuthResponse tokens = edgeMobileClient.getCombinedAccessTokens();
118:
119: // Extract the Access Token from the tokens object and assign
120: // it to the class variable, accessToken
121: accessToken = tokens.getMimikTokens().getAccessToken();
122: runOnUiThread(() -> {
123: Toast.makeText(
124: MainActivity.this,
125: "Got access token!",
126: Toast.LENGTH_LONG).show();
127: });
128:
129: // Deploy edge microservice now that an access token
130: // has been generated
131: deployRandomNumberMicroservice();
132: }
133: }
134: );
135: }
136:
137: private void deployRandomNumberMicroservice() {
138:
139: // Create microservice deployment configuration, dependent
140: // on microservice implementation
141: MicroserviceDeploymentConfig config = new MicroserviceDeploymentConfig();
142:
143: // set the name that will represent the microservice
144: config.setName("randomnumber-v1");
145:
146: // Get the tar file that represents the edge microservice
147: //but stored in the project's file system as a Gradle resource
148: config.setResourceStream(getResources().openRawResource(R.raw.randomnumber_v1));
149:
150: // Set the filename that by which the edge client will identify
151: // the microservice internally. This filename is associated internally
152: // with the resource stream initialized above
153: config.setFilename("randomnumber_v1.tar");
154:
155: // Declare the URI by which the application code will access
156: // the microservice
157: config.setApiRootUri(Uri.parse("/randomnumber/v1"));
158:
159: // Deploy edge microservice using the client library instance variable
160: MicroserviceDeploymentStatus status =
161: edgeMobileClient.deployEdgeMicroservice(accessToken, config);
162: if (status.error != null) {
163: // Display microservice deployment error
164: runOnUiThread(() -> {
165: Toast.makeText(
166: MainActivity.this,
167: "Failed to deploy microservice! " + status.error.getMessage(),
168: Toast.LENGTH_LONG).show();
169: });
170: } else {
171: // Store the microservice API root URI in the class variable,
172: // randomNumberRoot
173: randomNumberRoot = status.response.getContainer().getApiRootUri().toString();
174: // Display a message indicating a successful microservice deployment
175: runOnUiThread(() -> {
176: Toast.makeText(
177: MainActivity.this,
178: "Successfully deployed microservice!",
179: Toast.LENGTH_LONG).show();
180: });
181: }
182: }
183:
184: private void onGetClicked(View view) {
185: // Construct an API request for the edge microservice
186: OkHttpClient client = new OkHttpClient();
187: Request request = new Request.Builder()
188: .url(String.format(
189: "http://127.0.0.1:%d%s/randomNumber",
190: // use the client to get the default localhost port
191: edgeMobileClient.getEdgePort(),
192: randomNumberRoot)) // root URI determined by microservice deployment
193: .build();
194: client.newCall(request).enqueue(new Callback() {
195: @Override
196: public void onFailure(
197: @NotNull Call call,
198: @NotNull IOException e) {
199: // Display microservice request error
200: e.printStackTrace();
201: runOnUiThread(() -> {
202: Toast.makeText(
203: MainActivity.this,
204: "Failed to communicate with microservice! " + e.getMessage(),
205: Toast.LENGTH_LONG).show();
206: });
207: }
208:
209: @Override
210: public void onResponse(
211: @NotNull Call call,
212: @NotNull final Response response) throws IOException {
213: if (!response.isSuccessful()) {
214: // Display microservice unknown error
215: runOnUiThread(() -> {
216: Toast.makeText(
217: MainActivity.this,
218: "Microservice returned unexpected code! " + response,
219: Toast.LENGTH_LONG).show();
220: });
221: } else {
222: // Display microservice response
223: runOnUiThread(() -> {
224: try {
225: Toast.makeText(
226: MainActivity.this,
227: "Got " + response.body().string(),
228: Toast.LENGTH_LONG).show();
229: } catch (IOException e) {
230: e.printStackTrace();
231: }
232: });
233: }
234: }
235: });
236: }
237: }

If you've followed along by inserting and adding code as instructed throughout this tutorial, running the code is a matter of using the capabilities provided by Android Studio to deploy and run the code on your cellphone.

If for some reason you can't get your code up and running, you can use the working version of this code that ships in the cloned repository. It is in a branch named completed_code. You can run that code by cloning the repository for this demonstration project to a different location in your local file system. Then once the code is downloaded via cloning, go to the source code's working directory, open a terminal window in that directory and execute the following command:

git checkout completed_code.

Once you've checked out completed_code, open the project in Android Studio. Then, build, deploy and run the code on your device using Android Studio.

after-deployment
Figure 4: The demonstration application with the working randomization code from the edge microservice

Congratulations! You have completed the demonstration application that uses an edge microservice to provide behavior to fix the operational bug in application. Remember, the application was unable to display a random number each time the Get Random Number button was clicked. Now by binding the button click handler to make a call to the edge microservice, it does.

Review

In this document you learned the following:

  • How to configure the demonstration application to get the mimik Client Library for Android from a private Maven repository
  • How to configure the demonstration application with Client ID and Developer ID Token credentials
  • How to instantiate and start the mimik mimOE Runtime using the mimik Client Library for Android
  • How to use a Developer ID Token to authorize an mimOE Runtime and generate an Access Token
  • How to use a generated Access Token to deploy an edge microservice
  • How to make a request to an edge microservice to retrieve a random value
  • How to reset edgeEngine to return it to a like-new state

Was this article helpful?

© mimik technology, Inc. all rights reserved