📜 ⬆️ ⬇️

The interaction of Android devices on the local network



Suppose we are writing a game for Android, which implies some kind of networking between devices. Moreover, our devices are in the same network and we want the interaction between them to be carried out quickly, which means that the option of data exchange via the Internet does not suit us. Oh yeah, another little fly in the ointment - we want to reach the largest possible audience, for which we need to support Android 2.3.
What do we do? Let's talk about this, but at the same time we will consider the relatively new features of the Android SDK for connecting two or more devices.

What is it for and who is it for?


Once, having gone from a previous job and immersed in a well-deserved rest, I began to write a network game that people in one local network can play. And immediately faced with the fact that for the normal functioning of such a game, it is not enough for us to build network interaction - we need to make normal and fast detection of devices on the network. Actually, in this article I will share my experience in implementing solutions for this problem.
I’ll just say that the article is intended more for those who have experience in Android development, have written several applications and want to expand their horizons, as well as improve their professional skills.

What possible solutions exist?


  1. Android Network Service Discovery . Simple and effective way to detect devices. On Android Developer there is a step-by-step guide on how to connect NSD, there is an example of NsdChat, which can be downloaded in the same place. But there is one significant disadvantage - this method is supported only starting from API Level 16, that is, from Android 4.1 Jelly Bean;
  2. The second solution offered to us on the Android Developer website is Wi-Fi Peer-to-Peer . The problem with this method is the same - it is supported only starting from API Level 16;
  3. There is a strange solution, which is proposed by some programmers on Stack Overflow - to independently scan the local network for the presence of a server. That is, go to all network addresses. It already sounds like a strange bike, and now imagine that the port of our server is automatically assigned. Thus, scanning even the smallest network becomes quite a long and laborious task;
  4. Finally, we can look at the Java libraries and write something using them. For example, JmDNS .

The latter method looks quite adequate and seems to provide us with the required speed and convenience of detecting devices on the network for the end user.
')

So...


I armed myself with JmDNS and decided to try to build several classes that would simplify the writing of the applications described above to the maximum. But for starters, I had to slightly cut duplicate .class files from the JmDNS jar package (the problem is described here ):

mkdir unjar cd unjar jar xf ../jmdns.jar jar cfm ../jmdns.jar META-INF/MANIFEST.MF javax/ 


Next, I took the NsdChat source code from Android Developer and changed its utility class, which is responsible for initializing sockets and organizing network interaction. I also wrote a wrapper for JmDNS
 import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.util.Log; import java.io.IOException; import java.net.InetAddress; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceInfo; import javax.jmdns.ServiceListener; /** * @author alwx * @version 1.0 */ public class NetworkDiscovery { private final String DEBUG_TAG = NetworkDiscovery.class.getName(); private final String TYPE = "_alwx._tcp.local."; private final String SERVICE_NAME = "LocalCommunication"; private Context mContext; private JmDNS mJmDNS; private ServiceInfo mServiceInfo; private ServiceListener mServiceListener; private WifiManager.MulticastLock mMulticastLock; public NetworkDiscovery(Context context) { mContext = context; try { WifiManager wifi = (WifiManager) mContext.getSystemService(android.content.Context.WIFI_SERVICE); WifiInfo wifiInfo = wifi.getConnectionInfo(); int intaddr = wifiInfo.getIpAddress(); byte[] byteaddr = new byte[]{ (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) }; InetAddress addr = InetAddress.getByAddress(byteaddr); mJmDNS = JmDNS.create(addr); } catch (IOException e) { Log.d(DEBUG_TAG, "Error in JmDNS creation: " + e); } } /** * starts server with defined names on given port * * @param port server port */ public void startServer(int port) { try { wifiLock(); mServiceInfo = ServiceInfo.create(TYPE, SERVICE_NAME, port, SERVICE_NAME); mJmDNS.registerService(mServiceInfo); } catch (IOException e) { Log.d(DEBUG_TAG, "Error in JmDNS initialization: " + e); } } /** * performs servers discovery * * @param listener listener, that will be called after successful discovery * (see {@link me.alwx.localcommunication.connection.NetworkDiscovery.OnFoundListener} */ public void findServers(final OnFoundListener listener) { mJmDNS.addServiceListener(TYPE, mServiceListener = new ServiceListener() { @Override public void serviceAdded(ServiceEvent serviceEvent) { ServiceInfo info = mJmDNS.getServiceInfo(serviceEvent.getType(), serviceEvent.getName()); listener.onFound(info); } @Override public void serviceRemoved(ServiceEvent serviceEvent) { } @Override public void serviceResolved(ServiceEvent serviceEvent) { mJmDNS.requestServiceInfo(serviceEvent.getType(), serviceEvent.getName(), 1); } }); } /** * closes connection & unregisters all services */ public void reset() { if (mJmDNS != null) { if (mServiceListener != null) { mJmDNS.removeServiceListener(TYPE, mServiceListener); mServiceListener = null; } mJmDNS.unregisterAllServices(); } if (mMulticastLock != null && mMulticastLock.isHeld()) { mMulticastLock.release(); } } /** * accuires Wi-Fi lock */ private void wifiLock() { WifiManager wifiManager = (WifiManager) mContext.getSystemService(android.content.Context.WIFI_SERVICE); mMulticastLock = wifiManager.createMulticastLock(SERVICE_NAME); mMulticastLock.setReferenceCounted(true); mMulticastLock.acquire(); } public interface OnFoundListener { void onFound(ServiceInfo info); } } 


Here are 4 basic functions for Network Discovery:
  1. startServer to create a server and register the corresponding service in the local network;
  2. findServers to find servers;
  3. reset to finish working with Network Discovery and then free up resources;
  4. wifiLock to request a Wi-Fi block.


At the end, I wrote a universal class ConnectionWrapper for the full organization of detection, as well as messaging on the local network. Thus, the creation of a server in the final application is as follows:
 getConnectionWrapper().startServer(); getConnectionWrapper().setHandler(mServerHandler); 

And here is mServerHandler, used for receiving and processing messages:
 private Handler mServerHandler = new MessageHandler(MainActivity.this) { @Override public void onMessage(String type, JSONObject message) { try { if (type.equals(Communication.Connect.TYPE)) { final String deviceFrom = message.getString(Communication.Connect.DEVICE); Toast.makeText(getApplicationContext(), "Device: " + deviceFrom, Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { Log.d(DEBUG_TAG, "JSON parsing exception: " + e); } } }; 

Sending messages is even easier:
 getConnectionWrapper().send( new HashMap<String, String>() {{ put(Communication.MESSAGE_TYPE, Communication.ConnectSuccess.TYPE); }} ); 

And finally, the method to detect and connect to the server:
 private void connect() { getConnectionWrapper().findServers(new NetworkDiscovery.OnFoundListener() { @Override public void onFound(javax.jmdns.ServiceInfo info) { if (info != null && info.getInet4Addresses().length > 0) { getConnectionWrapper().stopNetworkDiscovery(); getConnectionWrapper().connectToServer( info.getInet4Addresses()[0], info.getPort(), mConnectionListener ); getConnectionWrapper().setHandler(mClientHandler); } } }); } 

As you can see, everything is very simple. And most importantly, it all works in any version of Android for up to two devices. But to make it work for a conditionally unlimited number of devices is very easy, and the obvious solution will come to you almost immediately after a detailed study of the Connection class. Let it be for homework.
Oh yes, all the code is available for everyone to learn and use in my repository on GitHub. . And, of course, I do not exclude the fact that some things can be done better and easier, so do not hesitate to fork and do pull requests.

Source: https://habr.com/ru/post/208132/


All Articles