In my opinion, this is very interesting and highly applicable API. Let's imagine, some Android devices which connected to the same wifi network or Ethernet data connection can send messages to together. In this post, you will learn how to set up applications for connecting multiple devices together over a network and how to send data over that connection.
Setting up Nearby API to Android Studio Project
build.gradle
file. At the time of writing, Nearby API 9.4.0 is the most recent release for development:
compile 'com.google.android.gms:play-services-nearby:9.4.0'
Adding checking network state permission to your AndroidManifest.xml
because this feature uses a LAN to communicate:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Next, you will need to add a piece of meta-data
in the application
tag that defines a service identifier that will be used by your app so that it can discover hosts advertising with that same identifier. In this example, our service identifier is defined in strings.xml
as service_id
:
<meta-data
android:name="com.google.android.gms.nearby.connection.SERVICE_ID"
android:value="@string/service_id" />
Now, go to your main activity Java code. This is the class where we will implement both advertising and discovery. In MainActivity
, you will also control sending messages between different devices.
Firstly, in order to start using the Nearby Connections API, you must set up and connect to the Google API Client. Implementing
ConnectionCallbacks
and OnConnectionFailedListener
at the top of your class. While we're adding our interfaces, let's also include the three that are needed by the API and an View.OnClickListener
to handle Buttons
event:
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
Connections.ConnectionRequestListener, Connections.MessageListener,
GoogleApiClient.ConnectionCallbacks,
Connections.EndpointDiscoveryListener, View.OnClickListener {
...
Like another Google Play Services API, in Android, you work with them through a GoogleApiClient
object. Initializing it in onCreate()
method of MainActivity
like this:
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Nearby.CONNECTIONS_API)
.build();
Start/Stop connecting in onStart()
/onStop()
:
@Override
protected void onStart() {
super.onStart();
googleApiClient.connect();
}
@Override
protected void onStop() {
super.onStop();
if (googleApiClient != null && googleApiClient.isConnected()) {
Nearby.Connections.stopAdvertising(googleApiClient);
googleApiClient.disconnect();
}
}
Let's now create the member variables that we'll need in the MainActivity
class. The most important here are two boolean flags for designating whether or not the device is connected and if it is the connection host, the GoogleApiClient
that is necessary for using the Nearby Connections API:
private String mRemoteHostEndpoint;
private GoogleApiClient googleApiClient;
private ListView listView;
private View btnConnect;
private View btnSend;
private ArrayAdapter<String> adapter;
private List<String> remotePeerEndpoints;
private TextInputLayout textInputLayout;
private View inputLayout;
private TextView status;
private RadioGroup radioGroup;
private long CONNECTION_TIME_OUT = 15000;
private boolean isConnected;
private boolean isHost = false;
Advertising connections
With your device, there are two connecting options: advertising and discovering. We'll go over the first option(advertising), which allows a device to assume the role of host and manage connections between various peers for communication. After checking your network, start advertising by call
Nearby.Connections.startAdvertising()
method. Adding some proper parameters and setResultCallback()
like this:
public boolean isConnectedToNetwork() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifiNetwork = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (wifiNetwork != null && wifiNetwork.isConnected()) {
return true;
}
NetworkInfo mobileNetwork = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (mobileNetwork != null && mobileNetwork.isConnected()) {
return true;
}
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isConnected()) {
return true;
}
return false;
}
private void advertise() {
if (!isConnectedToNetwork()) return;
String name = "Nearby Advertising";
Nearby.Connections.startAdvertising(googleApiClient, name, null, CONNECTION_TIME_OUT, this)
.setResultCallback(new ResultCallback<Connections.StartAdvertisingResult>() {
@Override
public void onResult(Connections.StartAdvertisingResult result) {
if (result.getStatus().isSuccess()) {
status.setText("Advertising");
}
}
});
}
Once your "host device" is advertising, it will be able to receive connection requests from peers. When a device attempts to connect, onConnectionRequest()
will be called. To accept the request, you call Nearby.Connections.acceptConnectionRequest
with a ResultCallback
. In this example, we'll add the remote endpoint to a list to keep track of it and broadcast to any connected peers that this new device has connected. Moreover, you determine that the device should not connect to your application, you can reject it by calling Nearby.Connections.rejectConnectionRequest
:
@Override
public void onConnectionRequest(final String remoteEndpointId, final String remoteDeviceId,
final String remoteEndpointName, byte[] payload) {
if (isHost) {
Nearby.Connections.acceptConnectionRequest(googleApiClient, remoteEndpointId, payload, this)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
if (!remotePeerEndpoints.contains(remoteEndpointId)) {
remotePeerEndpoints.add(remoteEndpointId);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
adapter.notifyDataSetChanged();
sendMessage(remoteDeviceId + " connected!");
inputLayout.setVisibility(View.VISIBLE);
}
}
});
} else {
Nearby.Connections.rejectConnectionRequest(googleApiClient, remoteEndpointId);
}
}
Discovering connections
Nearby.Connections.startDiscovery()
method, which sets your user's device into discovery mode.When a host device is detected,
onEndpointFound()
will be called. Request to connect with host by calling Nearby.Connections.sendConnectionRequest()
. If the connection is accepted or rejected by the host, the sendConnectionRequest()
result callback will be called. If the connection is accepted, we now prepare for sending messages across the connection channel:
private void discover() {
if (!isConnectedToNetwork())
return;
String serviceId = getString(R.string.service_id);
Nearby.Connections.startDiscovery(googleApiClient, serviceId, CONNECTION_TIME_OUT, this)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
MainActivity.this.status.setText("Discovering");
}
}
});
}
@Override
public void onEndpointFound(String endpointId, String deviceId, final String serviceId, String endpointName) {
byte[] payload = null;
Nearby.Connections.sendConnectionRequest(googleApiClient, deviceId, endpointId, payload,
new Connections.ConnectionResponseCallback() {
@Override
public void onConnectionResponse(String s, Status status, byte[] bytes) {
if (status.isSuccess()) {
MainActivity.this.status.setText("Connected to: " + s);
Nearby.Connections.stopDiscovery(googleApiClient, serviceId);
mRemoteHostEndpoint = s;
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
inputLayout.setVisibility(View.VISIBLE);
if (!isHost) {
isConnected = true;
}
} else {
MainActivity.this.status.setText("Connection to " + s + " failed");
if (!isHost) {
isConnected = false;
}
}
}
}, this);
}
Sending messages via the connection
onMessageReceived()
will be called. In this application, you will use this to display the payload as a string in a ListView
and, if the device is the host, rebroadcast it out to every connected device:
@Override
public void onMessageReceived(String s, byte[] bytes, boolean b) {
adapter.add(new String(bytes));
adapter.notifyDataSetChanged();
if (isHost) {
sendMessage(new String(bytes));
}
}
And this is method that sending a message (user input) via the connection, I divided into 2 cases: host device message and client device message:
private void sendMessage(String message) {
if (isHost) {
Nearby.Connections.sendReliableMessage(googleApiClient, remotePeerEndpoints, message.getBytes());
adapter.add(message);
adapter.notifyDataSetChanged();
} else {
Nearby.Connections.sendReliableMessage(googleApiClient, mRemoteHostEndpoint,
(Nearby.Connections.getLocalDeviceId(googleApiClient) + " says: " + message).getBytes());
}
}
Disconnecting from the connection
disconnectFromEndpoint()
and the API will handle severing the connection:
private void disconnect() {
if (!isConnectedToNetwork())
return;
if (isHost) {
sendMessage("Shutting down host");
Nearby.Connections.stopAdvertising(googleApiClient);
Nearby.Connections.stopAllEndpoints(googleApiClient);
isHost = false;
status.setText("Not connected");
remotePeerEndpoints.clear();
} else {
if (!isConnected || TextUtils.isEmpty(mRemoteHostEndpoint)) {
Nearby.Connections.stopDiscovery(googleApiClient, getString(R.string.service_id));
return;
}
sendMessage("Disconnecting");
Nearby.Connections.disconnectFromEndpoint(googleApiClient, mRemoteHostEndpoint);
mRemoteHostEndpoint = null;
status.setText("Disconnected");
}
isConnected = false;
}
Running application
RadioButton
and click the button to start connecting, status become "advertising":In the second device, choose "Client" and start connecting, the status change to "Discovering":
When the client device connected:
Sending some messages:
Conclusions
References:
- Nearby Connection API introduction on Google Developer site
- Nearby message video on Youtube