Androidのロングポーリング

記事を読んだ後、彼はウェブでロングポーリングプロジェクトを実装し始めました。 nginxでは、サーバー部分が回転し、javascriptでは、クライアントがチャネルをリッスンします。 まず、サイト上のプライベートメッセージに非常に役立ちました。

次に、Webプロジェクトをサポートするために、Android用のアプリケーションの開発が開始されました。 疑問が生じました。ブラウザクライアントとモバイルアプリケーションの両方が等しく参加するマルチユーザープロジェクトを実装する方法です。 ロングポーリングは既にブラウザバージョンに実装されているため、Android用のJavaモジュールも作成することにしました。



jsライブラリに完全に類似したものを作成するタスクはなかったので、特別な、しかし最も人気のあるケース用のモジュールの作成を開始しました。



サーバー側



そのため、サーバーから開始します。



Nginx


使用されているnginx-push-stream-module



nginx設定で:



#     location /channels-stats { push_stream_channels_statistics; push_stream_channels_path $arg_id; } #     location /pub { push_stream_publisher admin; push_stream_channels_path $arg_id; push_stream_store_messages on; #   ,   ,      } #     location ~ ^/lp/(.*) { push_stream_subscriber long-polling; push_stream_channels_path $1; push_stream_message_template "{\"id\":~id~,\"channel\":\"~channel~\",\"tag\":\"~tag~\",\"time\":\"~time~\",\"text\":~text~}"; push_stream_longpolling_connection_ttl 30s; }
      
      





構成には、メッセージの送信、受信、および統計の受信の3つのディレクティブが記述されています。



Php


メッセージはサーバーによって公開されます。 phpの関数の例を次に示します。



  /* * $cids - ID ,  ,     - ID  * $text - ,    */ public static function push($cids, $text) { $text = json_encode($text); $c = curl_init(); $url = 'http://example.com/pub?id='; curl_setopt($c, CURLOPT_RETURNTRANSFER, true); curl_setopt($c, CURLOPT_POST, true); $results = array(); if (!is_array($cids)) { $cids = array($cids); } $cids = array_unique($cids); foreach ($cids as $v) { curl_setopt($c, CURLOPT_URL, $url . $v); curl_setopt($c, CURLOPT_POSTFIELDS, $text); $results[] = curl_exec($c); } curl_close($c); }
      
      





ここでも、すべてが簡単です。送信するチャネルとメッセージを送信します。 プレーンプレーンテキストは誰にとっても面白くなく、素晴らしいJSON形式があるため、すぐにオブジェクトを送信できます。



チャンネル名形成の概念を考えるのに長い時間がかかりました。 異種のメッセージをすべてのクライアントに送信するか、1つまたは複数のクライアントに送信する可能性を提供する必要がありますが、何らかの基準でフィルタリングされます。



その結果、3つのパラメーターで構成される次の形式が開発されました。

 [id ]_[ ]_[id ]
      
      





すべてのユーザーにメッセージを送信する場合は、次のチャネルを使用します。

 0_main_0
      
      





id = 777のユーザーの場合:

 777_main_0
      
      





すべてに共通のリストでid = 777で順序値が変更された場合:

 0_orderPriceChanged_777
      
      





非常に柔軟であることがわかりました。

後で、少し考えてから、すべてのチャネルの盗聴でクライアントをロードするのではなく、この負荷を各ユーザーにメッセージを生成して送信するサーバーに転送する方が良いという結論に達しました。

また、メッセージタイプを分離するには、actなどのパラメーターを使用できます。

 const ACT_NEW_MESSAGE = 1; LongPolling::push($uid."_main_0", array( "act" => ACT_NEW_MESSAGE, "content" => "Hello, user ".$uid."!", ));
      
      







クライアント部



サーバー側については、すべてがそうです。 Javaに取り掛かりましょう!

gihubに投稿したクラス

私のライブラリでは、 android-async-httpライブラリを使用しました。これは便利な非同期http要求を提供します。 この例では、コンパイル済みのjarファイルを追加しました。



クラスのインターフェースは非常にシンプルです。

まず、コールバックオブジェクトを作成する必要があります。コールバックオブジェクトのメソッドは回答を受け取ります。 主にメッセージでオブジェクトを使用するため、クラスのコールバックとしてJsonHttpResponseHandlerを選択しました。

 private final static int ACT_NEW_ORDER = 1; private final static int ACT_DEL_ORDER = 2; private final static int ACT_ATTRIBUTES_CHANGED = 3; private final static int ACT_MESSAGE = 4; private final JsonHttpResponseHandler handler = new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { try { JSONObject json = response.getJSONObject("text"); switch (json.getInt("act")) { case ACT_NEW_ORDER: ... break; case ACT_DEL_ORDER: ... break; case ACT_ATTRIBUTES_CHANGED: ... break; case ACT_MESSAGE: ... break; default: break; } } catch (JSONException e) { e.printStackTrace(); } } };
      
      





この例では、新しい注文に関するメッセージ、注文の削除、ユーザー属性の変更、および新しいプライベートメッセージをリッスンします。



次に、LongPollingオブジェクトを初期化します(アクティビティでこれを行うとしましょう)。

 private LongPolling lp; private int uid = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_balance); lp = new LongPolling(getApplicationContext(), "http://example.com/lp/", Integer.toString(uid) + "_main_0", handler); }
      
      





アクティビティでロングポーリングのみが必要な場合は、登録する必要があります。

 public void onResume() { super.onResume(); lp.connect(); } public void onPause() { super.onPause(); lp.disconnect(); }
      
      





アプリケーション全体でメッセージを受信する必要がある場合(これが通常のケースです)、オブジェクトはアプリケーションクラス(Application)またはサービス(Service)で初期化できます。

次に、初期化の直後にリスニングを開始する必要があります

 lp = new LongPolling(getApplicationContext(), "http://example.com/lp/", Integer.toString(uid) + "_main_0", handler); lp.connect();
      
      





また、ユーザーのバッテリーを押し下げないようにするには、インターネット接続の出現/消失のイベントに対してBroadcastReceiverを登録する必要があります。

AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> ... <application> ... <receiver android:name="com.app.example.receivers.InternetStateReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" /> </intent-filter> </receiver> </application> </manifest>
      
      





およびInternetStateReceiver

 public class InternetStateReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); final android.net.NetworkInfo wifi = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); final android.net.NetworkInfo mobile = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if (wifi != null && wifi.isAvailable() || mobile != null && mobile.isAvailable()) { application.getInstance().lp.connect(); } else { application.getInstance().lp.disconnect(); } } }
      
      







統計



まあ、まんじゅうとして、リアルタイムの統計を見るのは面白いでしょう。

オンラインのユーザー数に興味があるとしましょう。

これを行うには、URLでXML / json情報を取得します。

 http://example.com/channels-stats?id=ALL
      
      





次を参照してください。

 <?xml version="1.0" encoding="UTF-8" ?> <root> <hostname>example.com</hostname> <time>2014-03-22T00:03:37</time> <channels>2</channels> <wildcard_channels>0</wildcard_channels> <uptime>818530</uptime> <infos> <channel> <name>4_main_0</name> <published_messages>0</published_messages> <stored_messages>0</stored_messages> <subscribers>1</subscribers> </channel> <channel> <name>23_main_0</name> <published_messages>0</published_messages> <stored_messages>0</stored_messages> <subscribers>1</subscribers> </channel> </infos> </root>
      
      





subscribersタグには、各チャンネルのリスナーの数が表示されます。 ただし、この場合、各ユーザーは独自のチャネルを持っているため、PHPではユーザーのリストをオンラインでコンパイルします。

 const STATISTICS_URL = 'http://example.com/channels-stats?id=ALL'; public static function getOnlineIds() { $str = file_get_contents(self::STATISTICS_URL); if (!$str) return; $json = json_decode($str); if (empty($json -> infos)) return; $ids = array(); foreach ($json->infos as $v) { if ($v -> subscribers > 0 && substr_count($v -> channel, '_main_0') > 0) { $ids[] = str_replace('_main_0', '', $v -> channel); } } return $ids; }
      
      





したがって、ネットワーク上のユーザーIDを取得しました。 しかし、1つだけあります。 クライアントがサーバーに再接続する時点では、統計には含まれません。これを考慮する必要があります。



おわりに



まあ、のような、すべてについて語った。 これで、同じ送信者を使用して、JavaScriptがメッセージを受け入れるブラウザーとAndroidのアプリケーションでユーザーにメッセージを送信できます。 さらに、たとえばサイト上で、オンラインの正確なユーザー数を導き出すことができます。



批判や提案を聞いてうれしいです。



参照資料


  1. https://github.com/jonasasx/LongPolling-実際には作業の結果
  2. https://github.com/wandenberg/nginx-push-stream-module-nxingモジュール
  3. https://github.com/loopj/android-async-http-Androidの非同期HTTPリクエストのライブラリ



All Articles