mirror of https://github.com/grpc/grpc-java.git
examples/android: add example for grpc running under StrictMode (#5527)
* added android example running under strictmode to detect grpc clear text network transport * renamed andriod strictmode example * use OkHttpChannelBuilder's api to feed with user's own transport executor * bump gradle and proto-plugin version to match that in master * add README with a simple demo image * Update README to use relative link to helloworld
This commit is contained in:
parent
0db0c637a1
commit
f25fe1fe6a
|
|
@ -0,0 +1,8 @@
|
|||
gRPC Android StrictMode Example
|
||||
========================
|
||||
|
||||
- This example intends to show the compatibility of gRPC with Android StrictMode.
|
||||
- Android SDK version 28 is required for [`StrictMode.VmPolicy.Builder.penaltyListener`](https://developer.android.com/reference/android/os/StrictMode.VmPolicy.Builder.html#penaltyListener(java.util.concurrent.Executor,%20android.os.StrictMode.OnVmViolationListener)) used in the example.
|
||||
- This example does the same thing as [HelloWorld example](../helloworld) except popping up a dialog for detected StrictMode policy violation (shown below).
|
||||
|
||||

|
||||
|
|
@ -0,0 +1,55 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.grpc.strictmodehelloworldexample"
|
||||
// API level 28 is required for StrictMode penaltyListener
|
||||
minSdkVersion 28
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
debug { minifyEnabled false }
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
disable 'GoogleAppIndexingWarning', 'HardcodedText', 'InvalidPackage'
|
||||
textReport true
|
||||
textOutput "stdout"
|
||||
}
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc { artifact = 'com.google.protobuf:protoc:3.7.0' }
|
||||
plugins {
|
||||
javalite { artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" }
|
||||
grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.21.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
}
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.plugins {
|
||||
javalite {}
|
||||
grpc { // Options added to --grpc_out
|
||||
option 'lite' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
|
||||
// You need to build grpc-java to obtain these libraries below.
|
||||
implementation 'io.grpc:grpc-okhttp:1.21.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
implementation 'io.grpc:grpc-protobuf-lite:1.21.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
implementation 'io.grpc:grpc-stub:1.21.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
implementation 'javax.annotation:javax.annotation-api:1.2'
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in $ANDROID_HOME/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
-dontwarn com.google.common.**
|
||||
# Ignores: can't find referenced class javax.lang.model.element.Modifier
|
||||
-dontwarn com.google.errorprone.annotations.**
|
||||
-dontwarn javax.naming.**
|
||||
-dontwarn okio.**
|
||||
-dontwarn sun.misc.Unsafe
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.grpc.strictmodehelloworldexample" >
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Base.V7.Theme.AppCompat.Light" >
|
||||
<activity
|
||||
android:name=".StrictModeHelloworldActivity"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright 2015 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.strictmodehelloworldexample;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.net.TrafficStats;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.StrictMode;
|
||||
import android.os.StrictMode.OnVmViolationListener;
|
||||
import android.os.StrictMode.VmPolicy;
|
||||
import android.os.strictmode.Violation;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.okhttp.OkHttpChannelBuilder;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StrictModeHelloworldActivity extends AppCompatActivity {
|
||||
private Button sendButton;
|
||||
private EditText hostEdit;
|
||||
private EditText portEdit;
|
||||
private EditText messageEdit;
|
||||
private TextView resultText;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
VmPolicy policy =
|
||||
new StrictMode.VmPolicy.Builder()
|
||||
.detectCleartextNetwork()
|
||||
.penaltyListener(
|
||||
MoreExecutors.directExecutor(),
|
||||
new OnVmViolationListener() {
|
||||
@Override
|
||||
public void onVmViolation(final Violation v) {
|
||||
runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new AlertDialog.Builder(StrictModeHelloworldActivity.this)
|
||||
.setTitle("StrictMode VM Violation")
|
||||
.setMessage(v.getLocalizedMessage())
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.penaltyLog()
|
||||
.build();
|
||||
StrictMode.setVmPolicy(policy);
|
||||
setContentView(R.layout.activity_strictmodehelloworld);
|
||||
sendButton = (Button) findViewById(R.id.send_button);
|
||||
hostEdit = (EditText) findViewById(R.id.host_edit_text);
|
||||
portEdit = (EditText) findViewById(R.id.port_edit_text);
|
||||
messageEdit = (EditText) findViewById(R.id.message_edit_text);
|
||||
resultText = (TextView) findViewById(R.id.grpc_response_text);
|
||||
resultText.setMovementMethod(new ScrollingMovementMethod());
|
||||
}
|
||||
|
||||
public void sendMessage(View view) {
|
||||
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
|
||||
.hideSoftInputFromWindow(hostEdit.getWindowToken(), 0);
|
||||
sendButton.setEnabled(false);
|
||||
resultText.setText("");
|
||||
new GrpcTask(this)
|
||||
.execute(
|
||||
hostEdit.getText().toString(),
|
||||
messageEdit.getText().toString(),
|
||||
portEdit.getText().toString());
|
||||
}
|
||||
|
||||
private static class GrpcTask extends AsyncTask<String, Void, String> {
|
||||
private final WeakReference<Activity> activityReference;
|
||||
private ManagedChannel channel;
|
||||
|
||||
private GrpcTask(Activity activity) {
|
||||
this.activityReference = new WeakReference<Activity>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
String host = params[0];
|
||||
String message = params[1];
|
||||
String portStr = params[2];
|
||||
int port = TextUtils.isEmpty(portStr) ? 0 : Integer.valueOf(portStr);
|
||||
try {
|
||||
channel =
|
||||
OkHttpChannelBuilder.forAddress(host, port)
|
||||
.transportExecutor(new NetworkTaggingExecutor(0xFDD))
|
||||
.usePlaintext()
|
||||
.build();
|
||||
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
|
||||
HelloRequest request = HelloRequest.newBuilder().setName(message).build();
|
||||
HelloReply reply = stub.sayHello(request);
|
||||
return reply.getMessage();
|
||||
} catch (Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.flush();
|
||||
return String.format("Failed... : %n%s", sw);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
try {
|
||||
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
Activity activity = activityReference.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
TextView resultText = (TextView) activity.findViewById(R.id.grpc_response_text);
|
||||
Button sendButton = (Button) activity.findViewById(R.id.send_button);
|
||||
resultText.setText(result);
|
||||
sendButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NetworkTaggingExecutor extends ThreadPoolExecutor {
|
||||
|
||||
private int trafficStatsTag;
|
||||
|
||||
NetworkTaggingExecutor(int tag) {
|
||||
super(
|
||||
0,
|
||||
Integer.MAX_VALUE,
|
||||
60L,
|
||||
TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>(),
|
||||
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("grpc-android-%d").build());
|
||||
trafficStatsTag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExecute(Thread t, Runnable r) {
|
||||
TrafficStats.setThreadStatsTag(trafficStatsTag);
|
||||
super.beforeExecute(t, r);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
TrafficStats.clearThreadStatsTag();
|
||||
super.afterExecute(r, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2015 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.grpc.examples.helloworld";
|
||||
option java_outer_classname = "HelloWorldProto";
|
||||
option objc_class_prefix = "HLW";
|
||||
|
||||
package helloworld;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<EditText
|
||||
android:id="@+id/host_edit_text"
|
||||
android:layout_weight="2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter Host" />
|
||||
<EditText
|
||||
android:id="@+id/port_edit_text"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberDecimal"
|
||||
android:hint="Enter Port" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/message_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter message to send" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/send_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="sendMessage"
|
||||
android:text="Send Grpc Request" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:textSize="16sp"
|
||||
android:text="Response:" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/grpc_response_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars = "vertical"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">GrpcStrictModeHelloworldExample</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.3.0'
|
||||
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()
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -0,0 +1 @@
|
|||
include ':app'
|
||||
Loading…
Reference in New Issue