はじめに
ネットワークに繋がって いる/いない のチェックは下記コードを使っていましたが、こちらは正確ではありませんでした。
if( Application.internetReachability != NetworkReachability.NotReachable )
公式に注意書きがありました。これは「機内モード」など、あくまで端末側の設定をチェックするもので、実際の接続をチェックするものではないとのこと。
注:このプロパティは、実際の接続性を判断するために使用しないでください。例:デバイスはホットスポットに接続されていても、ネットワークへの実際の経路を持っていないことがあります。
https://docs.unity3d.com/ScriptReference/Application-internetReachability.html
今回コードを見直したのでご紹介します。
サンプルコード
実際にネットワークに繋がっているかチェックするサンプルです。繋がっていればtrue、繋がっていなければfalseを返します。UniTaskを使います。
- Unity 2021.3.11
- UniTask 2.3.1
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;
using System;
//インターネット接続確認
public static class OnlineChecker {
//テストアクセスするURL
private static readonly string CHECK_TARGET_URL = "https://httpbin.org";
public async static UniTask<bool> IsOnline(int timeOut = 3000)
{
if(Application.internetReachability == NetworkReachability.NotReachable) return false;
bool bo = false;
var request = new UnityWebRequest(CHECK_TARGET_URL);
var task1 = request.SendWebRequest().ToUniTask();
var task2 = UniTask.Delay(timeOut);
try
{
DevLog.Log($"アクセス-開始 {CHECK_TARGET_URL}");
var result = await UniTask.WhenAny(task1,task2);
if(result.result.result == UnityWebRequest.Result.Success)
{
DevLog.Log($"アクセス-成功 {CHECK_TARGET_URL}");
bo = true;
}
}
catch(Exception e)
{
DevLog.LogWarning($"例外-失敗 {e}");
}
finally
{
await UniTask.WaitUntil(() => request.isDone);
request.Dispose();
DevLog.Log($"アクセス-廃棄");
}
return bo;
}
}
コード解説
OnlineChecker クラスはシーンに配置する必要はありません。
public static class OnlineChecker
テストアクセスしている「httpbin」は通信テストに便利なWebサービスです。本番では所持しているサーバーなどに置き換えてください。
private static readonly string CHECK_TARGET_URL = “https://httpbin.org”;
IsOnline() メソッドはタイムアウト用の int 値(デフォルト3秒)を引数にしていて、Unitak で Bool型を返しています。キャンセルは想定していていませんが、外部からキャンセルできるようにCancellationTokenを渡してもいいかもしれません。
public async static UniTask<bool> IsOnline( int timeOut = 3000 )
また、そもそも端末側でアクセス制限している場合はfalseを返します。
if(Application.internetReachability == NetworkReachability.NotReachable) return false;
Unityの過去バージョンではiOS14.2などでUnityWebRequest の timeoutが効かないことがあったようです。現在は修正されたようですが、、、
、、、私の環境ではtimeoutは機能しましたが、エラーを返してフリーズしてしまったので、UnityWebRequestをUnitask化して、UniTask.WhenAny()でUniTask.Delay()と掛け合わせて、時間を制限するようにしました。
var request = new UnityWebRequest(CHECK_TARGET_URL);
var task1 = request.SendWebRequest().ToUniTask();
var task2 = UniTask.Delay(timeOut);
var result = await UniTask.WhenAny(task1,task2);
UnityWebRequest をインスタンスしてtry-catch-finallyを使って、アクセス-成功/失敗/廃棄を処理しています。アクセスに失敗したら false 、成功したら true を返します。
try
{
DevLog.Log($”アクセス-開始 {CHECK_TARGET_URL}”);
var result = await UniTask.WhenAny(task1,task2);
if(result.result.result == UnityWebRequest.Result.Success)
{
DevLog.Log($”アクセス-成功 {CHECK_TARGET_URL}”);
bo = true;
}
}
catch(Exception e)
{
DevLog.LogWarning($”アクセス-失敗 {e}”);
}
finally
{
await UniTask.WaitUntil(() => request.isDone);
request.Dispose();
DevLog.Log($”アクセス-廃棄”);
}
return bo;
つまずいたのは、request.Dispose()するタイミングです。UniTask.WhenAnyが終了してから若干遅延があるようで、request.isDoneを待ってからDisposeするようにしました。
await UniTask.WaitUntil(() => request.isDone);
request.Dispose();
使い方
任意の場所からOnlineChecker.IsOnline()を呼び出して、bool値を受け取り、分岐させて処理を実行します。タイムアウトの時間を3秒から変更したい場合は数値を指定してください。
void Start()
{
_OnlineChecker().Forget();
}
//ネットワークチェック
async UniTaskVoid _OnlineChecker()
{
var bo = await OnlineChecker.IsOnline(2000);
if(bo)
{
Debug.Log("成功した処理");
}
else
{
Debug.Log("失敗した処理");
}
}