[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 이 조금 오래된 녀석이라.. )
1 2 3 4 5 | mGoogleApiClient = new GoogleApiClient.Builder( this ) .addConnectionCallbacks( this ) // GoogleApiClient.ConnectionCallback .addOnConnectionFailedListener( this ) // GoogleApiClient.OnConnectionFailedListener .addApi( Nearby.CONNECTIONS_API ) .build(); |
보통 onStart 에서 client 에 connect 를 하고 onStop 에서 client 를 disconnect 한다
1 2 3 4 5 6 7 8 9 10 11 12 13 | @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 을 호출해주면 된다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <p> // 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 } } }); }</p> |
-
host 에 연결하려는 시도를 받으면 아래 callback 이 불린다.
수락하려면 Nearby.Connections.acceptConnectionRequest, 거절하려면 Nearby.Connections.rejectConnectionRequest 를 호출하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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 를 검색할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 를 호출해 연결을 시도한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 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 가 불린다.
1 2 3 4 5 6 7 8 9 | // 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 를 가져올 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 한다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 |
댓글