- 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 edgeEngine Runtime
- The basics of using an Access Token to access and work with the edgeEngine 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.
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 edgeEngine 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 edgeEngine 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 edgeEngine 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.
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: ├── app3: │ ├── build.gradle //module level4: │ ├── proguard-rules.pro5: │ └── src6: │ ├── androidTest7: │ ├── main8: │ └── test9: ├── build.gradle //project level10: ├── gradle11: │ └── wrapper12: │ ├── gradle-wrapper.jar13: │ └── gradle-wrapper.properties14: ├── gradle.properties15: ├── gradlew16: ├── gradlew.bat17: ├── LICENSE18: ├── README.md19: └── 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 338:9: defaultConfig {10: applicationId "com.mimik.randomnumbergen"11: minSdkVersion 2612: targetSdkVersion 3313: versionCode 114: versionName "1.0"15:16: testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"17:18: manifestPlaceholders = ['appAuthRedirectScheme': ''] // Placeholder, for use with CustomTab authentication methods19: }20:21: buildTypes {22: release {23: minifyEnabled false24: proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'25: }26: }27: compileOptions {28: sourceCompatibility JavaVersion.VERSION_1729: targetCompatibility JavaVersion.VERSION_1730: }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. |
---|
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 338:9: defaultConfig {10: applicationId "com.mimik.randomnumbergen"11: minSdkVersion 2612: targetSdkVersion 3313: versionCode 114: versionName "1.0"15:16: testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"17:18: manifestPlaceholders = ['appAuthRedirectScheme': ''] // Placeholder, for use with CustomTab authentication methods19: 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 false27: proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'28: }29: }30: compileOptions {31: sourceCompatibility JavaVersion.VERSION_1732: targetCompatibility JavaVersion.VERSION_1733: }34: buildFeatures {35: buildConfig true36: }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:0.3.8.3'49: implementation "com.squareup.okhttp3:okhttp:4.9.3"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: <application6: 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: @Override14: 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: @Override40: 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 created35: @Override36: protected void onCreate(Bundle savedInstanceState) {37:38: }39:40: // Called with the Get Random Number is clicked41: private void onGetClicked(View view) {42:43: }44:45: // The code that will get and start the edgeEngine Runtime46: private void startEdge() {47:48: }49:50: // The code that will get the Access Token that's necessary to work51: // with the edge microservice running under the edgeEngine Runtime52: private void authorizeEdge() {53:54: }55:56: // The code that will deploy the edge microservice under the57: // edgeEngine Runtime58: 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 edgeEngine Runtime using the mimik Client Library automatically selects default parameters. This will have the edgeEngine Runtime host URLs using port 8083, and use the default license required to work with the edgeEngine 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 edgeEngine runtime6: edgeMobileClient = new EdgeMobileClient(this, new EdgeConfig());7:8: // Start edge using a new thread so as not to slow down activity creation9: 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 edgeEngine 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 edgeEngine Runtime up and running. The listing below shows the method startEdge()
. The method has the code that will initialize and start the edgeEngine 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 edgeEngine runtime3: 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 startup11: } 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 edgeEngine 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 settings3: String developerIdToken = BuildConfig.DEVELOPER_ID_TOKEN;4:5: String clientId = BuildConfig.CLIENT_ID; // The Client ID6:7: // Create mimik configuration object for Developer ID Token login8: DeveloperTokenLoginConfig config = new DeveloperTokenLoginConfig();9:10: // Set the root URL11: config.setAuthorizationRootUri(BuildConfig.AUTHORIZATION_ENDPOINT);12:13: // Set the value for the DEVELOPER_ID_TOKEN14: config.setDeveloperToken(developerIdToken);15:16: // Set the value for the CLIENT_ID17: config.setClientId(clientId);18:19: // Login to the edgeCloud20: edgeMobileClient.loginWithDeveloperToken(21: this,22: config,23: new EdgeResponseHandler() {24: @Override25: public void onError(EdgeRequestError edgeRequestError) {26: // Display error message27: 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 of36: // the method, edgeMobileClient.getCombinedAccessTokens()37: @Override38: public void onResponse(EdgeRequestResponse edgeRequestResponse) {39:40: // Get all the token that are stored within the41: // edgeMobileClient42: CombinedAuthResponse tokens = edgeMobileClient.getCombinedAccessTokens();43:44: // Extract the Access Token from the tokens object and assign45: // it to the class variable, accessToken46: 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 token55: // has been generated56: 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 edgeEngine 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, dependent4: // on microservice implementation5: MicroserviceDeploymentConfig config = new MicroserviceDeploymentConfig();6:7: // set the name that will represent the microservice8: config.setName("randomnumber-v1");9:10: // Get the tar file that represents the edge microservice11: //but stored in the project's file system as a Gradle resource12: config.setResourceStream(getResources().openRawResource(R.raw.randomnumber_v1));13:14: // Set the filename that by which the edge client will identify15: // the microservice internally. This filename is associated internally16: // with the resource stream initialized above17: config.setFilename("randomnumber_v1.tar");18:19: // Declare the URI by which the application code will access20: // the microservice21: config.setApiRootUri(Uri.parse("/randomnumber/v1"));22:23: // Deploy edge microservice using the client library instance variable24: MicroserviceDeploymentStatus status =25: edgeMobileClient.deployEdgeMicroservice(accessToken, config);26: if (status.error != null) {27: // Display microservice deployment error28: 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: // randomNumberRoot37: randomNumberRoot = status.response.getContainer().getApiRootUri().toString();38: // Display a message indicating a successful microservice deployment39: 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 microservice3: 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 port8: edgeMobileClient.getEdgePort(),9: randomNumberRoot)) // root URI determined by microservice deployment10: .build();11: client.newCall(request).enqueue(new Callback() {12: @Override13: public void onFailure(14: @NotNull Call call,15: @NotNull IOException e) {16: // Display microservice request error17: 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: @Override27: public void onResponse(28: @NotNull Call call,29: @NotNull final Response response) throws IOException {30: if (!response.isSuccessful()) {31: // Display microservice unknown error32: runOnUiThread(() -> {33: Toast.makeText(34: MainActivity.this,35: "Microservice returned unexpected code! " + response,36: Toast.LENGTH_LONG).show();37: });38: } else {39: // Display microservice response40: 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 edgeEngine Runtime, which we'll cover next.
Resetting the edgeEngine 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 edgeEngine Runtime that is supporting the microservice. Let's cover this topic now, briefly.
Resetting the edgeEngine Runtime will remove all authorization data and reset the authorization status. Resetting the edgeEngine Runtime also clears any data stored and and removes the deployed microservices. Finally, resetting stops the edgeEngine Runtime. The edgeEngine 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 dataExecutors.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 edgeEngine 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: @Override39: protected void onCreate(Bundle savedInstanceState) {40: super.onCreate(savedInstanceState);41: setContentView(R.layout.activity_main);42:43: // Instantiate new instance of edgeEngine runtime44: edgeMobileClient = new EdgeMobileClient(this, new EdgeConfig());45:46: // Start edge using a new thread so as not to slow down activity creation47: 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 edgeEngine runtime54: 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 startup62: } 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 settings78: String developerIdToken = BuildConfig.DEVELOPER_ID_TOKEN;79:80: String clientId = BuildConfig.CLIENT_ID; // The Client ID81:82: // Create mimik configuration object for Developer ID Token login83: DeveloperTokenLoginConfig config = new DeveloperTokenLoginConfig();84:85: // Set the root URL86: config.setAuthorizationRootUri(BuildConfig.AUTHORIZATION_ENDPOINT);87:88: // Set the value for the DEVELOPER_ID_TOKEN89: config.setDeveloperToken(developerIdToken);90:91: // Set the value for the CLIENT_ID92: config.setClientId(clientId);93:94: // Login to the edgeCloud95: edgeMobileClient.loginWithDeveloperToken(96: this,97: config,98: new EdgeResponseHandler() {99: @Override100: public void onError(EdgeRequestError edgeRequestError) {101: // Display error message102: 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 of111: // the method, edgeMobileClient.getCombinedAccessTokens()112: @Override113: public void onResponse(EdgeRequestResponse edgeRequestResponse) {114:115: // Get all the token that are stored within the116: // edgeMobileClient117: CombinedAuthResponse tokens = edgeMobileClient.getCombinedAccessTokens();118:119: // Extract the Access Token from the tokens object and assign120: // it to the class variable, accessToken121: 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 token130: // has been generated131: deployRandomNumberMicroservice();132: }133: }134: );135: }136:137: private void deployRandomNumberMicroservice() {138:139: // Create microservice deployment configuration, dependent140: // on microservice implementation141: MicroserviceDeploymentConfig config = new MicroserviceDeploymentConfig();142:143: // set the name that will represent the microservice144: config.setName("randomnumber-v1");145:146: // Get the tar file that represents the edge microservice147: //but stored in the project's file system as a Gradle resource148: config.setResourceStream(getResources().openRawResource(R.raw.randomnumber_v1));149:150: // Set the filename that by which the edge client will identify151: // the microservice internally. This filename is associated internally152: // with the resource stream initialized above153: config.setFilename("randomnumber_v1.tar");154:155: // Declare the URI by which the application code will access156: // the microservice157: config.setApiRootUri(Uri.parse("/randomnumber/v1"));158:159: // Deploy edge microservice using the client library instance variable160: MicroserviceDeploymentStatus status =161: edgeMobileClient.deployEdgeMicroservice(accessToken, config);162: if (status.error != null) {163: // Display microservice deployment error164: 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: // randomNumberRoot173: randomNumberRoot = status.response.getContainer().getApiRootUri().toString();174: // Display a message indicating a successful microservice deployment175: 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 microservice186: 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 port191: edgeMobileClient.getEdgePort(),192: randomNumberRoot)) // root URI determined by microservice deployment193: .build();194: client.newCall(request).enqueue(new Callback() {195: @Override196: public void onFailure(197: @NotNull Call call,198: @NotNull IOException e) {199: // Display microservice request error200: 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: @Override210: public void onResponse(211: @NotNull Call call,212: @NotNull final Response response) throws IOException {213: if (!response.isSuccessful()) {214: // Display microservice unknown error215: runOnUiThread(() -> {216: Toast.makeText(217: MainActivity.this,218: "Microservice returned unexpected code! " + response,219: Toast.LENGTH_LONG).show();220: });221: } else {222: // Display microservice response223: 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.
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 edgeEngine runtime using the mimik Client Library for Android
- How to use a Developer ID Token to authorize an edgeEngine 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