ローカルネットワークでのAndroidデバイスの相互作用





Android向けのゲームを作成しているとします。Androidには、デバイス間の何らかのネットワーク相互作用が含まれます。 さらに、私たちのデバイスは同じネットワーク上にあり、それらの間の相互作用を迅速に実行することを望んでいます。つまり、インターネット経由でデータを交換するオプションは私たちには適していません。 そうそう、軟膏の小さなハエ-Android 2.3をサポートする必要がある最大限の視聴者にリーチしたい。

どうする? これについて話しましょう。同時に、2つ以上のデバイスを接続するためのAndroid SDKの比較的新しい機能を検討してみましょう。



それは何についてで、誰のためですか?



以前、私の以前の職場を離れ、当然の休息に突入して、同じローカルネットワーク上の人々がプレイできるネットワークゲームを書き始めました。 そして、そのようなゲームが正常に機能するためには、ネットワークの相互作用を構築するだけでは不十分であるという事実にすぐに直面しました。ネットワーク上のデバイスを正常かつ迅速に検出する必要があります。 実際、この記事では、このタスクのソリューションを実装した経験を共有します。

この記事は、Android開発の経験があり、いくつかのアプリケーションを作成し、視野を広げ、専門的なスキルを向上させたい人を対象としています。



可能な解決策は何ですか?



  1. Androidネットワークサービスディスカバリー 。 デバイスを発見するためのシンプルで効果的な方法。 Android Developerには、NSDの接続に関する段階的なガイドがあり、そこにダウンロードできるNsdChatの例があります。 ただし、重大なマイナス点が1つあります。このメソッドは、APIレベル16以降、つまりAndroid 4.1 Jelly Beanでのみサポートされます。
  2. Androidデベロッパーサイトで提供される2番目のソリューションは、 Wi-Fiピアツーピアです。 このメソッドの問題は同じです-APIレベル16以降でのみサポートされています。
  3. 一部のStack Overflowプログラマーが提案する奇妙な解決策があります-サーバーのローカルネットワークを独立してスキャンすることです。 つまり、すべてのネットワークアドレスに移動します。 すでに奇妙なバイクのように聞こえますが、今ではサーバーのポートが自動的に割り当てられると想像してください。 したがって、最小のネットワークでさえスキャンするのは、かなり長く面倒な作業になります。
  4. 最後に、Javaライブラリに注意を払い、それらを使用して何かを書くことができます。 たとえば、 JmDNSです。


後者の方法は非常に適切に見えるため、エンドユーザーがネットワーク上でデバイスを見つけるために必要な速度と利便性を提供できるようです。



だから...



私はJmDNSで武装し、上記のアプリケーションの記述を最大限に簡素化するいくつかのクラスを構築しようとすることにしました。 しかし、まず第一に、JmDNS jarパッケージから少し重複した.classファイルを削除する必要がありました(問題はここで説明されています )。



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







次に、Android DeveloperからNsdChatソースコードを取得し、そのユーティリティクラスを変更しました。このユーティリティクラスは、ソケットの初期化とネットワーク相互作用の整理を担当します。 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); } }
      
      







ネットワーク探索の4つの主な機能は次のとおりです。

  1. サーバーを作成し、対応するサービスをローカルネットワークに登録するstartServer
  2. サーバーを見つけるためのfindServers
  3. ネットワーク検出を終了し 、後でリソースを解放するためにリセットします。
  4. Wi-Fiロックを要求するwifiLock




結論として、ローカルネットワーク上のメッセージングだけでなく、ディスカバリーの完全な組織のためのユニバーサルConnectionWrapperクラスを作成しました。 したがって、最終アプリケーションでのサーバーの作成は次のとおりです。

 getConnectionWrapper().startServer(); getConnectionWrapper().setHandler(mServerHandler);
      
      





また、メッセージの受信と処理に使用されるmServerHandlerは次のとおりです。

 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); } } };
      
      





メッセージの送信はさらに簡単です:

 getConnectionWrapper().send( new HashMap<String, String>() {{ put(Communication.MESSAGE_TYPE, Communication.ConnectSuccess.TYPE); }} );
      
      





最後に、サーバーを検出して接続する方法:

 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); } } }); }
      
      





ご覧のとおり、すべてが非常に簡単です。 そして最も重要なことは、これらはすべて、Androidのどのバージョンでも最大2台のデバイスで機能することです。 しかし、条件付きで無制限の数のデバイスで機能させるのは非常に簡単で、Connectionクラスを詳細に検討した後、すぐに明らかな解決策が得られます。 宿題としてしましょう。

そうです、すべてのコードは、 私のGitHubリポジトリのすべての人が学習および使用するために利用可能です。 。 そして、もちろん、私はいくつかのことをより良く簡単に行える可能性を排除していないので、forしてプル要求をすることをheしないでください。



All Articles