2011年10月8日土曜日

Wi-Fi設定周りの簡易実装検討など

 

AndroidのWi-Fi設定周りについて考えたことの備忘録です。

Wi-Fi設定いじって何するの?

先日のことですが「TwitterStreamingAPIはモバイルネットワークで非推奨なんだよプンプーン」みたいな話を知りました。

おきにいり: Twitter Streaming APIの仕様が変わっていたのでびびった件 <http://miquniqu.blogspot.com/2011/10/twitter-streaming-api.html>

なので、早速StreamingAPIの呼び出し前にWi-Fi接続判定処理を組み込んでみました。
バックグラウンドサービス(AlarmManager+Service起動)からStreamingAPIを呼び出すアプリなのでとりあえず放置。

放置してたので画面は自動的にオフされました。

サービス起動時間も過ぎてから結果確認したところ、受信された形跡がない。
どうもWi-Fiがオンにならないので受信処理ができないようですね。
デバッグ中には発生しない問題なので、なぜWi-Fiが切断されるんだろうと例によって少し考え込んじゃいましたが、しばらくして理由が分かりました。

AndroidのOS設定で「Wi-Fiのスリープ設定-画面がOFF時になったとき」になってた、、、orz orz orz

Wi-Fiのことしらなすぎじゃね?というわけで、今回はWi-Fiの話です。  

Wi-Fi設定画面

AndroidのWi-Fi設定画面ってOS毎、メーカー毎に結構カスタマイズされてるみたいですね。

Xperia SO-01B(OS2.1)の場合

image

ICONIA TAB A500(OS3.1)の場合

image  

ちなみにREGZA Phoneなんかは、スリープ中にWi-Fi接続できないといった時期が合ったらしい。

レグザフォン:スリープ中でもWifi接続を維持する - れぐぽんといっしょ <http://myregzaphone.qlookblog.net/2011/0112.html>

今はOSバージョンがあがって対策されたみたいです。(よくしらない
対策アプリとしてリリースされたレグポンが5万DLであることを考えると、Wi-Fiが切断されると困る層がいるんだなって思います。
※もちろんREGZA Phone以外からのDLもあるのだろうけど。

Wi-Fi接続のための条件

AndroidOSの標準設定は「ユーザが端末操作しているときだけWi-Fi接続する」といった方針のようです。
→根拠:Wi-Fiのスリープ設定のデフォルトが「画面がOFF時になったとき」になっている。

今作っているアプリはユーザ操作してないとき(画面オフ時)もWi-Fi接続されてないと要件を満たしません。
では、Wi-Fi接続を利用するための条件をざっくりと整理してみましょう。

①アクセスポイントの用意(外部環境)
②アクセスポイントの設定(AndroidOS設定)
③Wi-Fi接続ルール(スリープ時切断など)(AndroidOS設定)
④Wi-Fi設定のON/OFF(AndroidOS設定)
⑤Wi-Fi接続強度(外部環境)
⑥上記を踏まえた結果として、最終的にどの通信IFで接続されているか?

もっと細かい話はあると思いますが、シビアなWi-Fi専用アプリじゃなければ
このぐらいでいいんじゃないでしょうか。

アプリでどこまでやるか

上記の①~③はユーザ管理の設定作業なのでアプリ提供者としては無視していきたい。
特にアクセスポイントの有無までは確認したくない。
する場合は以下のサイトのWi-Fi関連が参考になるかもしれません。

WiFiのAPをスキャンする - Androidプログラマへの道 ~ Moonlight 明日香 ~ - livedoor Wiki(ウィキ) <http://wiki.livedoor.jp/moonlight_aska/d/WiFi%A4%CEAP%A4%F2%A5%B9%A5%AD%A5%E3%A5%F3%A4%B9%A4%EB#>

③は今回Wi-Fi調べる要因にはなったけど、アプリからはどうしようもないよなってことです。しいて言えば、気がつき安いようになぜ失敗したかユーザにわかるような情報提供をするとか?

④はソースコード上からWifiManager.isWifiEnabled()とすれば判断できます。
そのうえで、WifiManager.setWifiEnabled(true)とすれば設定が可能です。

⑤は動的な環境としての問題。
ソフト検知可能かもしれないけれど、強度をどう扱うかはアプリの性質によると考えます。
今回のアプリはつながってさえいればいいので無視です。
強度が弱いなら結果的に接続エラーが発生して検知できるでしょう。

⑥は「外部環境さえ用意できたらOS設定としてはWi-Fi接続可能ですよ」となった段階で確認します。
接続有無を判定した上で、接続先がWi-Fiか判定すればよい?

あと、正常→異常方向は検出時のエラー処理を行い、異常→正常方向で復旧の対処をどうするかはアプリの性質によると思います。
今回のアプリでは復旧はユーザ任せにしたい。

Wi-Fi利用機能の考え方

もろもろの話を踏まえて、簡単に実装検討します。

検討するアプリの前提
・Wi-Fi設定中のみ動作可能な機能がある
・バッググラウンドで動作する機能である
・Wi-FiのOS設定自体はユーザによって行なわれているものとする
・画面OFF時のWi-Fi接続設定などはもユーザによって行なわれているものとする

Wi-Fiの確認としてやりたいことを簡単にまとめるとこうなります。
・OS設定がWi-Fi接続可能となっているか?
・実際に接続されているか?
・それはモバイル、Wi-Fiのどちらか?

ではそのWi-Fi利用機能の事前確認ロジック

①isWifiEnabled()でWi-Fi設定状態を確認
 →ONなら設定済みであるとし、接続状態確認へ→③
 →OFFなら設定が必要(かどうかはアプリによるが)→②
②setWifiEnabled(true)でWi-Fi設定する
 加えて、設定結果の反映ラグを考慮して30秒Sleep後に接続状態確認へ→③
③接続状態を確認する
 →「接続状態が接続無し」ならあきらめる
 →「接続状態が接続有りかつWi-Fi以外」ならあきらめる
 →「接続状態が接続有りかつWi-Fi」ならアプリ独自の処理へ

あと、Wi-Fi利用中に切断された場合は、エラー検知時にアプリの性質に沿って運用する。
・停止して最初からやり直し
・一定回数一定間隔でリトライ
など。

実装例

NetworkUtil.java=WifiManagerの処理をまとめた簡易ユーティリティクラス
WiFiAccess.java=それを利用する側の処理抜粋

NetworkUtil.java

  1: package com.miquniqu.util;
  2: 
  3: import android.content.Context;
  4: import android.net.ConnectivityManager;
  5: import android.net.NetworkInfo;
  6: import android.net.wifi.WifiManager;
  7: 
  8: public class NetworkUtil {
  9:   /**
 10:    * ネットワーク接続しているか判定する
 11:    */
 12:   public static boolean isConnected(Context context) {
 13:     ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 14:     NetworkInfo networkInfo = cm.getActiveNetworkInfo();
 15:     if (networkInfo != null) {
 16:       return networkInfo.isConnected();
 17:     }
 18:     return false;
 19:   }
 20: 
 21:   /**
 22:    * Wi-Fi接続しているか判定する
 23:    */
 24:   public static boolean isWifiConnected(Context context) {
 25:     ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 26:     NetworkInfo networkInfo = cm.getActiveNetworkInfo();
 27:     if (networkInfo != null) {
 28:       if (cm.getActiveNetworkInfo().isConnected()) {
 29:         if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
 30:           return true;
 31:         }
 32:       }
 33:     }
 34:     return false;
 35:   }
 36: 
 37:   /**
 38:    * Wi-Fi設定しているか判定する
 39:    * 未設定の場合には、設定後に指定ミリ秒スリープする
 40:    */
 41:   public static void checkWifiEnabled(Context context, long time) {
 42:     WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
 43:     if (!wifi.isWifiEnabled()) {
 44:       //Wifi未設定なら設定
 45:       wifi.setWifiEnabled(true);
 46:       try {
 47:         // 反映ラグを待つ
 48:         Thread.sleep(time);
 49:       } catch (InterruptedException ie) {
 50:         // 無視でいいかな
 51:       }
 52:     }
 53:   }
 54: 
 55:   /**
 56:    * ネットワーク接続先情報を取得する
 57:    */
 58:   public static String getConnecteType(Context context) {
 59:     ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 60:     NetworkInfo networkInfo = cm.getActiveNetworkInfo();
 61:     if (networkInfo != null) {
 62:       if (cm.getActiveNetworkInfo().isConnected()) {
 63:         if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
 64:           // Wifi に接続している
 65:           return "Wi-Fi Connected";
 66:         } else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
 67:           // モバイルネットワークに接続している
 68:           return "Mobile Connected";
 69:         } else {
 70:           // その他接続
 71:           return "Other Connected";
 72:         }
 73:       } else {
 74:         return "Not Connected";
 75:       }
 76:     } else {
 77:       return "";
 78:     }
 79:   }
 80: }
 81: 

WiFiAccess.java(抜粋)

  1:   // Wi-Fi設定チェックする。未設定の場合は設定して30秒スリープ
  2:   NetworkUtil.checkWifiEnabled(getApplicationContext(), 30000);
  3:   // 接続状態判定
  4:   boolean connect = NetworkUtil.isWifiConnected(getApplicationContext());
  5:   if (connect) {
  6:     ~ここでアプリ用の機能
  7:   }
  8: 
  9: 


これで、家に返って何もしてなくても勝手にWi-Fi設定に切り替えて処理を行うバックグラウンドサービスになりました。
忘れっぽい自分には必須ですね。


ちょっと懸念事項


そういえば、Wi-Fi接続環境だけでStreamingAPI使うってのは理解できたのだけど、間にモバイルネットワーク挟んでテザリングなんかされた日には結局同じだよなぁ。
テザリング環境で利用しないでねってどこかで書かないとだめかなー。ああー。

0 件のコメント:

コメントを投稿