I'm building an android application and I have some second thoughts on some error handling case.
I have a method that gets data from the internet by calling this method:
public static string StoredDatesList
{
get => Preferences.Get(nameof(StoredDatesList), string.Empty);
set => Preferences.Set(nameof(StoredDatesList), value);
}
public static async Task<string> GetDraws(Uri url, string date)
{
Dictionary<string, string> StoredDates = new Dictionary<string, string>();
StoredDates = JsonConvert.DeserializeObject<Dictionary<string, string>>(StoredDatesList);
var contents = string.Empty;
HttpClient client = new HttpClient();
if (StoredDates != null)
if (StoredDates.ContainsKey(date))
{
contents = StoredDates[date];
}
else
{
var current = Connectivity.NetworkAccess;
if (current != NetworkAccess.Internet)
return null;
client = new HttpClient();
contents = await client.GetStringAsync(url);
var res2 = JsonConvert.DeserializeObject<RootObject>(contents.ToString());
if (180 == res2.content.Count)
{
StoredDates.Add(date, contents);
StoredDatesList = JsonConvert.SerializeObject(StoredDates, Formatting.Indented);
}
}
else
{
StoredDates = new Dictionary<string, string>();
contents = await client.GetStringAsync(url);
var res2 = JsonConvert.DeserializeObject<RootObject>(contents.ToString());
if (180 == res2.content.Count)
{
StoredDates.Add(date, contents);
StoredDatesList = JsonConvert.SerializeObject(StoredDates, Formatting.Indented);
}
}
return contents;
}
the if
statement current != NetworkAccess.Internet
checks if internet is available. When internet is not available I return null and I check if the data is null and display a message(error, internet is not available etc).
I find this approach very bad and I'm trying to think how is the proper way to handle this. I cannot show a message to the user from the GetDraws()
function.
Maybe the correct way for this approach is to have a public variable like bool internetError = false;
and to make if false every time I call GetDraws()
, make it true if internet is not available and check its state after GetDraws()
?
Or should I return as result of GetDraws()
the error and check first if the result match of any errors?
Internet connection is not necessary every time GetDraws()
is used and that is why I'm not checking before I called this function for internet connection
EDIT: My implementation i decided to be this:
#region Setting Error Constants
public const string Common_Error_NoInternetConnection = "Error_NoInternetConnection";
#endregion
static MainApplication ApplicationState = (MainApplication)Application.Context;
public static string StoredDatesList
{
get => Preferences.Get(nameof(StoredDatesList), string.Empty);
set => Preferences.Set(nameof(StoredDatesList), value);
}
public static async Task<string> GetDraws(Uri url, string date)
{
var StoredDates = JsonConvert.DeserializeObject<Dictionary<string, string>>(StoredDatesList);
var contents = string.Empty;
if (StoredDates == null)
StoredDates = new Dictionary<string, string>();
if (StoredDates.ContainsKey(date))
{
contents = StoredDates[date];
}
else
{
if (IsInternetEnabled())
contents = await DownloadResults(url, date, StoredDates, contents).ConfigureAwait(false);
else
return Settings.Common_Error_NoInternetConnection;
}
return contents;
}
private static bool IsInternetEnabled()
{
return Connectivity.NetworkAccess == NetworkAccess.Internet;
}
private static async Task<string> DownloadResults(Uri url, string date, Dictionary<string, string> StoredDates, string contents)
{
using (var client = ApplicationState.GlobalHTTPClient)
{
contents = await client.GetStringAsync(url).ConfigureAwait(false);
var res2 = JsonConvert.DeserializeObject<RootObject>(contents);
if ((int)DailyDrawsEnum.AllDraws == res2.content.Count)
{
StoredDates.Add(date, contents);
StoredDatesList = JsonConvert.SerializeObject(StoredDates, Formatting.Indented);
}
return contents;
}
}
public class MainApplication : Application
{
// Global objects to share data between activities
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public HttpClient GlobalHTTPClient { get; } = new HttpClient(new HttpRetryMessageHandler(new HttpClientHandler()));
}
public class HttpRetryMessageHandler : DelegatingHandler
{
public HttpRetryMessageHandler(HttpClientHandler handler) : base(handler) { }
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken) =>
Policy
.Handle<HttpRequestException>()
.Or<TaskCanceledException>()
.OrResult<HttpResponseMessage>(x => !x.IsSuccessStatusCode)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)))
.ExecuteAsync(() => base.SendAsync(request, cancellationToken));
}
And when internet is not available i return a string error code which i check if exist and if yes, i display a message to the user:
contents = await GetDrawsFunctions.GetDraws(url, dates[datesPos]);
if (contents == Settings.Common_Error_NoInternetConnection)
{
((MainActivity)Activity).ShowNoInternetConnectionSnackbarMessage();
return -1;
}
GetDraws()
is to accomplish (the source code would have been a favourable place). \$\endgroup\$HttpClient
is intended to be instantiated once per application, rather than per-use. And you don't need to call.ToString()
forstring
members becausestring.ToString()
has no sense if you pass no arguments there. \$\endgroup\$