https://github.com/liuyuweitarek/simple_sample_grpc
Step-by-Step Guide to Implementing a Simple gRPC Connection between Android (Client) and Python (Server)
https://github.com/liuyuweitarek/simple_sample_grpc
android-client grpc java python-server python3
Last synced: 6 months ago
JSON representation
Step-by-Step Guide to Implementing a Simple gRPC Connection between Android (Client) and Python (Server)
- Host: GitHub
- URL: https://github.com/liuyuweitarek/simple_sample_grpc
- Owner: liuyuweitarek
- License: mit
- Created: 2021-11-20T16:37:08.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-11-21T16:19:36.000Z (over 4 years ago)
- Last Synced: 2025-03-01T05:26:25.939Z (about 1 year ago)
- Topics: android-client, grpc, java, python-server, python3
- Language: Python
- Homepage: https://medium.com/@liuyuwei.tarek/step-by-step-implement-simple-sample-of-grpc-connection-android-as-client-and-python-as-server-78eb3dbad600
- Size: 8.84 MB
- Stars: 3
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Simple-Sample-gRPC : Implement gRPC Connection: Android as Client | Python as Server
## Demo

## Quick Start
1. Clone Project
```bash
$ git clone https://github.com/liuyuweitarek/Simple_Sample_gRPC.git
```
2. Start Python gRPC Server
```bash
$ cd Simple_Sample_gRPC/Python_gRPC_Server
$ python run.py
```
3. Install Android App
If you use Android emulator and set up python server on your localhost, please input 10.0.2.2 in App UI.
## Step by Step
Follow the steps on the gRPC documents. ([https://grpc.io/docs/languages/python/quickstart/](https://grpc.io/docs/languages/python/quickstart/)) and record some details here.
1. Define your proto file and place it under your android project folder.
e.g.
```bash
$ mkdir ./Simple_gRPC/Android_gRPC_Client/src/main/proto
$ vim interaction.proto
```
interaction.proto
```protobuf
/**
* Send Message between Android APP Client and Python Server.
*/
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.interaction.robot.interaction";
option java_outer_classname = "InteractProto";
option objc_class_prefix = "ST";
package interaction;
// The interact service definition.
service Interact {
// Sends connection confirm
rpc SimpleConnect (ConnectRequest) returns (ConnectReply) {}
// Sends information
rpc SimpleSend (Input) returns (Output) {}
}
// The connect request message contains the status
message ConnectRequest {
string status = 1;
}
// The connect response message contains the status
message ConnectReply {
string status = 1;
}
// The input message contains the utterance
message Input {
string utterance = 1;
}
// The output info message contains the utterance
message Output {
string utterance = 1;
}
```
2. Using python module "grpcio" and "grpcio-tools" to generate your python server configuration and protocol objects.
```bash
$ pip install grpcio grpcio-tools
```
```bash
python -m grpc_tools.protoc --proto_path={.proto_filepath} {.proto filename} --python_out={generated_python_filepath} --grpc_python_out={generated_python_grpc__filepath}
# (e.g.)
# $ cd to the .proto filepath
# $ python -m grpc_tools.protoc --proto_path=. ./interaction.proto --python_out=../../../../Python_gPRC_Server/. --grpc_python_out=../../../../Python_gPRC_Server/.
```
3. How to use Python Server
In last step, we generated two files, *_pb2.py and *_pb2_grpc.py, which contained objects of python gRPC protocol and settings. In the purpose of using them in a clean way, we'll define a new server object and set up a server folder.
- Python_gRPC_Server
- interaction_pb2.py
- interaction_pb2_grpc.py
- InteracterServer.py
Define Server which maintains the connection settings and API usage.
```python
"""
File: InteracterServer.py
Author: Yuwei Liu
Institution: Modeling and Informatics Laboratory
Version: v0.1.0
"""
import os
import grpc
import time
from random import randrange
from concurrent import futures
import socket
import logging
import json
import interaction_pb2
import interaction_pb2_grpc
class Server(interaction_pb2_grpc.InteractServicer):
"""
This is the only class in the interacter package.
It creates a gRPC server with the computer's local ip address with a specified port.
"""
logging.basicConfig(level=os.environ.get("LOGLEVEL","INFO"))
log = logging.getLogger(__name__)
is_connected = False
client_type = ''
client_ip = ''
client_command = None
client_response = ''
ERR_EMPTY_ARGS = 'Cannot have empty args!'
def __init__(self, port):
"""
The constructor for Server class.
Arguments:
port (int): The port where the server will be listening for incoming client connections.
"""
self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
interaction_pb2_grpc.add_InteractServicer_to_server(self, self.server)
self.server.add_insecure_port('[::]:' + str(port))
self.log.info('gRPC server started on address '+ self.get_ip() + ':' + str(port))
self.server.start()
def SimpleConnect(self, request, context):
"""
This method is called when a client connects to the server.
"""
self.is_connected = True
handshake = json.loads(request.status)
self.client_type = handshake["intent"]
self.client_ip = handshake["value"]
return interaction_pb2.ConnectReply(status="I got it. --from Python Server")
def SimpleSend(self, request, context):
"""
This method is called every time a message is received from the client.
If the robot returns an error message, it will be printed in the log.
"""
json_response = json.loads(request.utterance)
if 'value' in json_response:
self.client_response = json_response['value']
else:
self.client_response = None
if json_response['intent'] == 'error':
self.log.warning(self.client_response)
self.client_command = None
while self.client_command == None:
time.sleep(.2)
return interaction_pb2.Output(utterance=self.client_command)
def Other_Client_API(self, value_to_client=value):
output = {'intent': 'client_function', 'value': str(value)}
self.client_command = json.dumps(output)
while self.client_command != None:
time.sleep(0.1)
return self.client_response
```
- run.py
Here we could use the APIs and add other services
```python
"""
File: interacter.py
Author: Yuwei Liu
Institution: Modeling and Informatics Laboratory
Version: v0.1.0
"""
from InteracterServer import Server
import time
import os
import sys
import argparse
from threading import Thread
import subprocess
class MainLoop(object):
def __init__(self, args):
self.path = os.path.dirname(os.path.abspath(__file__))
self.server = Server(50051)
#Wait until the robot has connected
while not self.server.is_connected:
time.sleep(1)
print('client connected: ' + self.server.client_type)
def start(self):
self.server.other_client_function(value="haha")
input("Success Start...")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
args = parser.parse_args()
mainLoop = MainLoop(args)
mainLoop.start()
```
4. Set Android Java Client
Please notify TAG "// --/>" in the files below.
- Import gRPC to Global Gradle
./Simple_gRPC/build.gradle :
```groovy
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
//
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
//--/>
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
```
- Import gRPC to APP Gradle
The version of gRPC in this sample is 1.4.0.
./Simple_gRPC/Android_gRPC_Client/build.gradle:
```groovy
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf' //
//
ext {
grpcVersion = '1.4.0'
}
//--/>
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "ntu.mil.simple_grpc"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
//
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.3.0'
}
plugins {
javalite {
artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
}
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc {
option 'lite'
}
}
}
}
}
//--/>
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
//
// For Dealing with Command
implementation 'com.google.code.gson:gson:2.8.5'
//gRPC Client
implementation "io.grpc:grpc-okhttp:${grpcVersion}"
implementation "io.grpc:grpc-protobuf-lite:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
implementation 'javax.annotation:javax.annotation-api:1.3.2'
protobuf 'com.google.protobuf:protobuf-java:3.3.1'
//--/>
```
- Add Network permission
./Simple_gRPC/Android_gRPC_Client/src/main/AndroidManifest.xml:
```xml
//
//--/>
```
- Setup UI
./Simple_gRPC/Android_gRPC_Client/src/main/res (could just watch the code)
- Setup Android gRPC Client Connection
./src/main/java/SimpleGrpcActivity.java (could just watch the code)
```java
public void connect(){
mIp = ip_1.getText().toString() + "."
+ ip_2.getText().toString() + "."
+ ip_3.getText().toString() + "."
+ ip_4.getText().toString();
mPort = Integer.parseInt(host_port.getText().toString());
Log.i(TAG,"Attempting to connect to server with ip " + mIp);
try {
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(mIp, mPort)
.usePlaintext(true)
.build();
mInteracter = InteractGrpc.newBlockingStub(managedChannel);
String jsonHandshake = new Gson().toJson(new ServerCommand("Android APP", mLocalIp.getText().toString()));
ConnectRequest connectRequest = ConnectRequest.newBuilder().setStatus(jsonHandshake).build();
ConnectReply connectReply = mInteracter.simpleConnect(connectRequest);
mConnectState = connectReply.getStatus();
mTextState.setText(mConnectState);
} catch (Exception e) {
Log.e(TAG, "connect() Fail: " + e);
mTextState.setText("connect() Fail: " + e);
}
}
```
- Setup Protocol setters and getters
./src/main/java/ServerCommand.java (could just watch the code)
```java
public class ServerCommand {
private String intent;
private String value;
public ServerCommand(String intent, String value){
this.intent = intent;
this.value = value;
}
public String getIntent() {
return intent;
}
public void setIntent(String intent) {
this.intent = intent;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "ServerCommand{" +
"intent='" + intent + '\'' +
", value='" + value + '\'' +
'}';
}
}
```