[android] Google Play Service: Nearby Connections API |
https://code.tutsplus.com/tutorials/google-play-services-using-the-nearby-connections-api--cms-24534
-
Google Play Service(이하 G.P.S) 에 있는 Nearby Connections API 를 이용하면,
LAN ( Local Area Network ) 를 이용해 App 을 host 로 작동시키면서 여러개의 device 를 해당 host 에 연결시킬 수 있다.
Use case 는 phone 을 android TV 에 연결해서 control 을 한다던지, Multi user 를 연결해서 game 을 한다던지 할 수 있겠다.
1. Project Setup
-
gradle compile 추가
compile 'com.google.android.gms:play-services:7.5.0’
-
LAN 을 사용하기 위해서 아래 permission 이 필요하다.
MOS 의 runtime permission 은 알아서 잘 대응 할 수 있을 것이라 믿는다.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
연결을 위한 일종의 Key 인 SERVICE_ID 가 필요하다.
이 녀석을 manifest 의 application 에 정의해야 한다.
<meta-data android:name="com.google.android.gms.nearby.connection.SERVICE_ID" android:value="@string/service_id" />
-
Nearby Connections API 뿐만 아니라 G.P.S. API 를 사용하기 위해서는 대부분 GoogleApiClient 연결이 필요하다.
( 최근 Lib 들은 이것을 잘 hide 하는 편이지만, 해당 article 이 조금 오래된 녀석이라.. )
mGoogleApiClient = new GoogleApiClient.Builder( this )
.addConnectionCallbacks( this ) // GoogleApiClient.ConnectionCallback
.addOnConnectionFailedListener( this ) // GoogleApiClient.OnConnectionFailedListener
.addApi( Nearby.CONNECTIONS_API )
.build();보통 onStart 에서 client 에 connect 를 하고 onStop 에서 client 를 disconnect 한다
@Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
@Override
protected void onStop() {
super.onStop();
if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
mGoogleApiClient.disconnect();
}
}2. Advertising and Accepting Connections
-
Advertising 은 host 가 “나 여기있으니 연결할 사람 연결해요” 라고 광고하는 것이다.
advertising 시작은 Nearby.Connections.startAdvertising 을 호출해주면 된다
// LAN 만 사용한다 private static int[] NETWORK_TYPES = {ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_ETHERNET }; private boolean isConnectedToNetwork() { ConnectivityManager connManager = (ConnectivityManager) getSystemService( Context.CONNECTIVITY_SERVICE ); for( int networkType : NETWORK_TYPES ) { NetworkInfo info = connManager.getNetworkInfo( networkType ); if( info != null && info.isConnectedOrConnecting() ) { return true; } } return false; } private void advertise() { if( !isConnectedToNetwork() ) return; String name = "Nearby Advertising”; // 마지막 this 는 Connections.ConnectionRequestListener, Nearby.Connections.startAdvertising( mGoogleApiClient, name, null, CONNECTION_TIME_OUT, this ) .setResultCallback( new ResultCallback<Connections.StartAdvertisingResult>() { @Override public void onResult( Connections.StartAdvertisingResult result ) { if( result.getStatus().isSuccess() ) { // Advertising success } } }); }
-
host 에 연결하려는 시도를 받으면 아래 callback 이 불린다.
수락하려면 Nearby.Connections.acceptConnectionRequest, 거절하려면 Nearby.Connections.rejectConnectionRequest 를 호출하면 된다.
// Connections.ConnectionRequestListener 의 callback
public void onConnectionRequest(final String remoteEndpointId, final String remoteDeviceId, final String remoteEndpointName, byte[] payload) {
if( mIsHost ) {
// 마지막 this 는 Connections.MessageListener,
Nearby.Connections.acceptConnectionRequest( mGoogleApiClient, remoteEndpointId, payload, this ).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if( status.isSuccess() ) {
if( !mRemotePeerEndpoints.contains( remoteEndpointId ) ) {
mRemotePeerEndpoints.add( remoteEndpointId );
}
mMessageAdapter.add(remoteDeviceId + " connected!");
mMessageAdapter.notifyDataSetChanged();
sendMessage(remoteDeviceId + " connected!");
mSendTextContainer.setVisibility( View.VISIBLE );
}
}
});
} else {
Nearby.Connections.rejectConnectionRequest(mGoogleApiClient, remoteEndpointId );
}
}
3. Discovery
-
Nearby.Connections.startDiscovery 를 호출하면 host 를 검색할 수 있다.
private void discover() {
if( !isConnectedToNetwork() )
return;
String serviceId = getString( R.string.service_id );
// 마지막 this 는 Connections.EndpointDiscoveryListener
Nearby.Connections.startDiscovery(mGoogleApiClient, serviceId, 10000L, this).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
mStatusText.setText( "Discovering" );
} else {
Log.e( "TutsPlus", "Discovering failed: " + status.getStatusMessage() );
}
}
});
}-
Host 를 찾으면 아래 Callback 이 불린다.
Nearby.Connections.sendConnectionRequest 를 호출해 연결을 시도한다.
// Connections.EndpointDiscoveryListener callback 중 하나
public void onEndpointFound(String endpointId, String deviceId, final String serviceId, String endpointName) {
byte[] payload = null;
// 마지막 this 는 Connections.MessageListener 이다
Nearby.Connections.sendConnectionRequest( mGoogleApiClient, deviceId, endpointId, payload, new Connections.ConnectionResponseCallback() {
@Override
public void onConnectionResponse(String endpointId, Status status, byte[] bytes) {
if( status.isSuccess() ) {
mStatusText.setText( "Connected to: " + endpointId );
Nearby.Connections.stopDiscovery(mGoogleApiClient, serviceId);
mRemoteHostEndpoint = endpointId;
mSendTextContainer.setVisibility(View.VISIBLE);
if( !mIsHost ) {
mIsConnected = true;
}
} else {
mStatusText.setText( "Connection to " + endpointId + " failed" );
if( !mIsHost ) {
mIsConnected = false;
}
}
}
}, this );
}연결 전에 host 가 advertising 을 그만둔 경우 onEndpointLost method 가 불리고,
에러로 인해 제대로 된 연결이 안 되었을 경우(disconnect 가 된 경우) onDisconnected method 가 불린다.
4. Sending Messages
-
Message 는 reliable 한 TCP 와 unreliable 한 UDP 가 있다.
Message 가 도착한 경우 onMessageReceived 가 불린다.
// Connections.MessageListener 의 callback
public void onMessageReceived(String endpointId, byte[] payload, boolean isReliable) {
mMessageAdapter.add( new String( payload ) );
mMessageAdapter.notifyDataSetChanged();
if( mIsHost ) {
sendMessage( new String( payload ) );
}
} -
sendMessage 는 필자가 구현한 method 로 안쪽에서는 Nearby.Connections.sendReliableMessage 혹은 Nearby.Connections.sendUnreliableMessage 를 통해 메시지를 보낸다.
접속하는 device 들은 Nearby.Connections.getLocalDeviceId 를 통해 본인의 id 를 가져올 수 있다.
private void sendMessage( String message ) {
if( mIsHost ) {
Nearby.Connections.sendReliableMessage( mGoogleApiClient,
mRemotePeerEndpoints,
message.getBytes() );
mMessageAdapter.add( message );
mMessageAdapter.notifyDataSetChanged();
} else {
Nearby.Connections.sendReliableMessage( mGoogleApiClient,
mRemoteHostEndpoint,
( Nearby.Connections.getLocalDeviceId( mGoogleApiClient ) + " says: " + message ).getBytes() );
}
}5. Disconnecting
-
host 에서는 Nearby.Connections.stopAdvertising 과 Nearby.Connections.stopAllEndPoints 를 통해 disconnect 하고,
peer 에서는 Nearby.Connections.stopDiscovery, Nearby.Connections.disconnectFromEndpoint 를 통해 disconnect 한다
private void disconnect() {
if( !isConnectedToNetwork() )
return;
if( mIsHost ) {
sendMessage( "Shutting down host" );
Nearby.Connections.stopAdvertising( mGoogleApiClient );
Nearby.Connections.stopAllEndpoints( mGoogleApiClient );
mIsHost = false;
mStatusText.setText( "Not connected" );
mRemotePeerEndpoints.clear();
} else {
if( !mIsConnected || TextUtils.isEmpty( mRemoteHostEndpoint ) ) {
Nearby.Connections.stopDiscovery( mGoogleApiClient, getString( R.string.service_id ) );
return;
}
sendMessage( "Disconnecting" );
Nearby.Connections.disconnectFromEndpoint( mGoogleApiClient, mRemoteHostEndpoint );
mRemoteHostEndpoint = null;
mStatusText.setText( "Disconnected" );
}
mIsConnected = false;
}-
끝!
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
| [android] Lazy loading dex files (0) | 2019.01.30 |
|---|---|
| [android] "Memory leak" detect library (0) | 2019.01.29 |
| [android] background work(AlarmManager) 수행에 대한 이야기 (0) | 2019.01.27 |
| [android] Chrome Custom Tabs (3) | 2019.01.26 |
| [android] PercentLayout (0) | 2019.01.25 |
댓글