[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 |
댓글