Detectando conexões de Internet WiFi, 3G/4G e Conexão de rede em Android utilizando Delphi

Olá.

Hoje precisei garantias que um recurso bastante comum estava disponível para um projeto que estou trabalhando, a internet. Em dispositivos móveis podemos utiliza-la usando conexões de rede WiFi e/ou 3G/4G. Então para que o aplicativo não levante uma exceção já que é necessário verificar os dados no servidor, preciso garantias que ela esteja disponível em uma destas formas.
Já trabalhei em projetos nativos utilizando o Android Studio e este é meu primeiro trabalho profissional usando o Delphi para dispositivos móveis, e no geral tenho gostado, contudo verificar a disponibilidade me deixou meio decepcionado, pois precisei de uma Biblioteca de terceiros (desenvolvida por um MVP da Embarcadeiro que não recordo o nome) pra fazer isso e sinceramente acho que merecia um recurso nativo. Bom pode ser que exista, mas eu não encontrei ninguém falando disto. 
Então para resolver o problema utilizei a seguinte biblioteca.

unit NetworkState;

interface

uses
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.CoreFoundation,
  //Macapi.SystemConfiguration,
  Macapi.Dispatch,
  iOSApi.CocoaTypes,
  iOSApi.Foundation,
  Posix.SysSocket;
{$ENDIF}
{$IFDEF ANDROID}
  System.SysUtils,
  FMX.Helpers.Android,
  Androidapi.JNIBridge,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.JavaTypes,
  Androidapi.Helpers;
{$ENDIF}
{$IFDEF IOS}

const
  libSystemConfiguration =
    '/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration';
  //libReachability = '/usr/lib/system/libReachability.dylib';

  kSCNetworkFlagsConnectionAutomatic = 8;
  kSCNetworkFlagsConnectionRequired = 4;
  kSCNetworkFlagsInterventionRequired = 16;
  kSCNetworkFlagsIsDirect = 131072;
  kSCNetworkFlagsIsLocalAddress = 65536;
  kSCNetworkFlagsReachable = 2;
  kSCNetworkFlagsTransientConnection = 1;
  kSCNetworkReachabilityFlagsConnectionAutomatic = 8;
  kSCNetworkReachabilityFlagsConnectionOnDemand = 32;
  kSCNetworkReachabilityFlagsConnectionOnTraffic = 8;
  kSCNetworkReachabilityFlagsConnectionRequired = 4;
  kSCNetworkReachabilityFlagsInterventionRequired = 16;
  kSCNetworkReachabilityFlagsIsDirect = 131072;
  kSCNetworkReachabilityFlagsIsLocalAddress = 65536;
  kSCNetworkReachabilityFlagsReachable = 2;
  kSCNetworkReachabilityFlagsTransientConnection = 1;
  kSCNetworkReachabilityFlagsIsWWAN = $40000;
{$ENDIF}

type
{$IFDEF IOS}
  SCNetworkReachabilityFlags = UInt32;

  SCNetworkReachabilityRef = ^__SCNetworkReachability;

  __SCNetworkReachability = record
  end;

  SCNetworkReachabilityContext = record
    version: CFIndex;
    info: Pointer;
    retain: function(info: Pointer): Pointer;
    release: procedure(info: Pointer);
    copyDescription: function(info: Pointer): CFStringRef;
  end;

  SCNetworkReachabilityContextPtr = ^SCNetworkReachabilityContext;

  SCNetworkReachabilityCallback = procedure(target: SCNetworkReachabilityRef;
    flags: SCNetworkReachabilityFlags; info: Pointer);

  TReachability = class;

  Reachability = interface(NSObject)
    ['{B405394F-57B1-4FF1-83D9-8FBFA38FFD7B}']
    function startNotifier: LongBool; cdecl;
    procedure stopNotifier; cdecl;
    function isReachable: LongBool; cdecl;
    function isReachableViaWWAN: LongBool; cdecl;
    function isReachableViaWiFi: LongBool; cdecl;
    function isConnectionRequired: LongBool; cdecl;
    function connectionRequired: LongBool; cdecl;
    function isConnectionOnDemand: LongBool; cdecl;
    function isInterventionRequired: LongBool; cdecl;
    function currentReachabilityStatus: NSInteger; cdecl;
    function reachabilityFlags: SCNetworkReachabilityFlags; cdecl;
    function currentReachabilityString: NSString; cdecl;
    function currentReachabilityFlags: NSString; cdecl;
  end;

  ReachabilityClass = interface(NSObjectClass)
    ['{39EC0490-2787-4BB9-95EA-77BB885BFD01}']
    function reachabilityWithHostname(hostname: NSString): Pointer; cdecl;
    function reachabilityForInternetConnection: Pointer; cdecl;
    function reachabilityWithAddress: Pointer; cdecl;
    function reachabilityForLocalWiFi: Pointer; cdecl;
  end;

  TReachability = class(TOCGenericImport)
  end;

function SCNetworkReachabilityCreateWithAddress(allocator: CFAllocatorRef;
  address: psockaddr): SCNetworkReachabilityRef; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithAddress';
function SCNetworkReachabilityCreateWithAddressPair(allocator: CFAllocatorRef;
  localAddress: psockaddr; remoteAddress: psockaddr): SCNetworkReachabilityRef;
  cdecl; external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithAddressPair';
function SCNetworkReachabilityCreateWithName(allocator: CFAllocatorRef;
  nodename: PChar): SCNetworkReachabilityRef; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithName';
function SCNetworkReachabilityGetTypeID: CFTypeID; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilityGetTypeID';
function SCNetworkReachabilityGetFlags(target: SCNetworkReachabilityRef;
  var flags: SCNetworkReachabilityFlags): Boolean; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilityGetFlags';
function SCNetworkReachabilitySetCallback(target: SCNetworkReachabilityRef;
  callout: SCNetworkReachabilityCallback;
  var context: SCNetworkReachabilityContext): Boolean; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilitySetCallback';
function SCNetworkReachabilityScheduleWithRunLoop
  (target: SCNetworkReachabilityRef; runLoop: CFRunLoopRef;
  runLoopMode: CFStringRef): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityScheduleWithRunLoop';
function SCNetworkReachabilityUnscheduleFromRunLoop
  (target: SCNetworkReachabilityRef; runLoop: CFRunLoopRef;
  runLoopMode: CFStringRef): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityUnscheduleFromRunLoop';
function SCNetworkReachabilitySetDispatchQueue(target: SCNetworkReachabilityRef;
  queue: dispatch_queue_t): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilitySetDispatchQueue';

{$IFDEF CPUARM}
function FakeLoader: Reachability; cdecl;
  external 'libReachability.a' name 'OBJC_CLASS_$_Reachability';
{$ENDIF}
{$ENDIF}
{$IFDEF ANDROID}
JConnectivityManager = interface;
JNetworkInfo = interface;

JNetworkInfoClass = interface(JObjectClass)
  ['{E92E86E8-0BDE-4D5F-B44E-3148BD63A14C}']
end;

[JavaSignature('android/net/NetworkInfo')]
JNetworkInfo = interface(JObject)['{6DF61A40-8D17-4E51-8EF2-32CDC81AC372}']
{ Methods }
function isAvailable: Boolean;
cdecl;

function isConnected: Boolean; cdecl;
  function isConnectedOrConnecting: Boolean; cdecl;
  end;
TJNetworkInfo = class(TJavaGenericImport)
end;

JConnectivityManagerClass = interface(JObjectClass)
  ['{E03A261F-59A4-4236-8CDF-0068FC6C5FA1}']
{ Property methods }
function _GetTYPE_WIFI: Integer;
cdecl;

function _GetTYPE_WIMAX: Integer; cdecl;
  function _GetTYPE_MOBILE: Integer; cdecl;
  { Properties }
  property TYPE_WIFI: Integer read _GetTYPE_WIFI;
  property TYPE_WIMAX: Integer read _GetTYPE_WIMAX;
  property TYPE_MOBILE: Integer read _GetTYPE_MOBILE;
  end;

[JavaSignature('android/net/ConnectivityManager')]
JConnectivityManager = interface(JObject)
  ['{1C4C1873-65AE-4722-8EEF-36BBF423C9C5}']
{ Methods }
function getActiveNetworkInfo: JNetworkInfo;
cdecl;

function getNetworkInfo(networkType: Integer): JNetworkInfo; cdecl;
end;
TJConnectivityManager = class(TJavaGenericImport
  JConnectivityManager>)
end;
{$ENDIF}

type
  TMobileNetworkStatus = class(TObject)
  public
    constructor Create;
    destructor Destroy; override;
    function isConnected: Boolean;
    function IsWiFiConnected: Boolean;
    function IsMobileConnected: Boolean;
  end;

implementation

{$IFDEF ANDROID}

function GetConnectivityManager: JConnectivityManager;
var
  ConnectivityServiceNative: JObject;
begin
  ConnectivityServiceNative := SharedActivityContext.getSystemService
    (TJContext.JavaClass.CONNECTIVITY_SERVICE);
  if not Assigned(ConnectivityServiceNative) then
    raise Exception.Create('Could not locate Connectivity Service');
  Result := TJConnectivityManager.Wrap
    ((ConnectivityServiceNative as ILocalObject).GetObjectID);
  if not Assigned(Result) then
    raise Exception.Create('Could not access Connectivity Manager');
end;
{$ENDIF}
{$IFDEF IOS}

function GetInternetReachability: Reachability;
begin
  Result := TReachability.Wrap
    (TReachability.OCClass.reachabilityForInternetConnection);
end;
{$ENDIF}

constructor TMobileNetworkStatus.Create;
begin
end;

destructor TMobileNetworkStatus.Destroy;
begin
  inherited;
end;

function TMobileNetworkStatus.isConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  ActiveNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachable;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  ActiveNetwork := ConnectivityManager.getActiveNetworkInfo;
  Result := Assigned(ActiveNetwork) and ActiveNetwork.isConnected;
{$ENDIF}
end;

function TMobileNetworkStatus.IsMobileConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  MobileNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachableViaWWAN;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  MobileNetwork := ConnectivityManager.getNetworkInfo
    (TJConnectivityManager.JavaClass.TYPE_MOBILE);
  Result := MobileNetwork.isConnected;
{$ENDIF}
end;

function TMobileNetworkStatus.IsWiFiConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  WiFiNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachableViaWiFi;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  WiFiNetwork := ConnectivityManager.getNetworkInfo
    (TJConnectivityManager.JavaClass.TYPE_WIFI);
  Result := WiFiNetwork.isConnected;
{$ENDIF}
end;

initialization

{$IFDEF IOS}
{$IFDEF CPUARM}
if False then
  FakeLoader;
{$ENDIF}
{$ENDIF}

end.

Bom aqui no post ficou bem feio e pouco didático este código todo, mas se um dia precisar disso novamente já sei onde esta e não precisarei ficar pesquisando novamente.

Bom para utilizar esta unit é necessário copia-la pra pasta do seu projeto e em seguida adiciona-la ao mesmo. Conforme figura abaixo:

Feito isto basta utilizar o Use Unit e selecionar NetworkState na parte do projeto onde ira testar a disponibilidade da internet. Especificamente neste projeto foi implementado no FormCreate e evidentemente talvez não seja o melhor evento, mas apenas para demonstração foi usando assim. Veja figura abaixo.:

Nestre caso o método getDadosRest vai ser disparado apenas se for detectado sinal de internet através dos canais WiFi e 3G/4G, caso contrario carregará os dados existentes (se houver) no device.

Apesar de ser uma solução de terceiros funcionou muito bem, nos testes executados e mostrou-se boa alternativa dado a sua simplicidade de uso.

Se tiver interessado em uma tema especifico, sugere ai nos comentários.

Até mais. 





Comentários

Postagens mais visitadas deste blog

Preenchendo com zeros a esquerda de um valor com Firebird

AWS para Desenvolvedores - Dominando S3 com Delphi

Conhecendo o FireDac - trabalhando com Macros