2017-08-28 3 views
0

Mon application Android Xamarin utilise un service Web auquel elle se connecte via HttpClient. Sur aucune connexion (par exemple lorsque l'utilisateur n'a pas de connexion cellulaire ou WiFi), une exception est levée. J'utilise async/await pour obtenir les données du serveur. Voici un extrait de mon code:Afficher AlertDialog sur l'exception HttpClient (Xamarin Android)

public async Task<String> doLogin(string username, string password) 
    { 
     String url = Constants.loginEndpoint + username + "/" + password + "/"; 
     var uri = new Uri(string.Format(url, string.Empty)); 

     return_string = ""; 

     try 
     { 
      var response = await GetAsync(uri); 
      if (response.IsSuccessStatusCode) 
      { 
       return_string = "success"; 
       // Process the positive response here 
      else 
      { } 
     } 
     catch (Exception ex) 
     { 
      throw new ConnectionException(); 
     } 

     return return_string; 
    } 

I défini un ConnectionException de custon et que vous voulez montrer un AlertDialog à l'utilisateur de les informer, que la demande a échoué en raison de pas de connexion. Après que l'utilisateur clique sur OK, je souhaite fermer l'application. J'ai essayé de montrer la boîte de dialogue d'alerte de la manière suivante, mais ça ne marche pas:

public class ConnectionException : Exception 
{ 
    public ConnectionException() 
    { 
     AlertDialog.Builder alert = new AlertDialog.Builder(myApp.Context); 
     alert.SetTitle("Failure"); 
     alert.SetMessage("Request failed. No connection."); 
     alert.SetPositiveButton("OK", (senderAlert, args) => 
     { 
     }); 

     Dialog dialog = alert.Create(); 
     dialog.Show(); 
    } 

    public ConnectionException(string message) 
     : base(message) 
    { } 

    public ConnectionException(string message, Exception innerException) 
     : base(message, innerException) 
    { } 
} 

Est-ce la bonne approche? Probablement pas, car ça ne marche pas. J'apprécierais toute aide sur la façon d'y parvenir. De plus, je n'y ai pas trop réfléchi, mais est-ce une façon privilégiée de gérer de telles exceptions?

+1

l'objet d'exception lui-même ne devrait pas être responsable de l'affichage d'un message. Vous essayez également (éventuellement) d'afficher une alerte d'interface utilisateur sur un thread d'arrière-plan. – Jason

Répondre

0

En supposant que votre myApp.Context est un Activity et il n'a pas pile arrière, vous pouvez simplement appeler Finish()

var context = myApp.Context; // this needs to be an Activity-based context... 
context.RunOnUiThread(() => 
{ 
    var alertDialog = new AlertDialog.Builder(context) 
     .SetTitle("Failure") 
     .SetMessage("Request failed. No connection.") 
     .SetPositiveButton("OK", (senderAlert, args) => 
     { 
      context.Finish(); 
     }) 
     .Create(); 
    alertDialog.Show(); 
}); 
+0

C'est la bonne solution! Merci! – jkwi

0

Est-ce que vous réutilisez cette exception à plusieurs endroits, ou est-ce une exception? Si vous n'utilisez cette exception qu'une seule fois, il n'y a aucune raison réelle de créer la vôtre. Vous pouvez tout aussi bien capturer l'exception et poster votre alerte depuis votre capture.

Je sais que ce n'est pas aussi joli d'écrire le catch, mais si ça marche, pourquoi ne pas l'utiliser.

Note latérale: DisplayAlert peut être plus facile pour vous aussi. Ce sera un seul paquebot.

Exemple:

await DisplayAlert("Failure","Request failed. No connection.", "Ok"); 
+0

DisplayAlert est XF, il fait juste Android – Jason

+0

Je suis conscient. Je ne suis pas sûr s'ils utilisaient déjà XF dans l'application. Si c'est le cas, il suffit d'utiliser DisplayAlert, autrement, ils ignorent probablement cette partie. – Hyren123

0

La façon dont vous gérez les erreurs possibles contient de multiples problèmes et n'est pas la bonne approche pour plusieurs raisons. Premièrement: Votre code ne suit pas les conventions C-Sharp et contient plusieurs odeurs de code. Je vous montre un style meilleur et plus accepté.

1) Les méthodes en C# commencent normalement par une lettre majuscule. doLogin devient Login

2) Pour créer une nouvelle instance Uri, vous n'avez pas besoin de formater votre URL. La chaîne.Empty ne sera pas utilisée. Le code peut donc être simplifié en await GetAsync(new Uri(...));

3) La méthode return_string ne semble pas être utilisée en dehors de la méthode. C'est string.Empty ou "succès". Pourquoi ne pas le changer en bool? De cette façon, vous pouvez facilement vérifier si la connexion a réussi. Le type de retour devient booléen au lieu de chaîne.

La méthode ressemble maintenant à ceci:

public async Task<bool> Login(string username, string password) 
{ 
    //TODO: Do parameter check for username and password 

    try 
    { 
     var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/")); 

     if (response.IsSuccessStatusCode) 
     { 
      // Process the positive response here 

      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 
    catch (Exception ex) 
    { 
     throw new ConnectionException(); 
    } 

    return false; 
} 

En second lieu, comme mentionné par @ Jason, une exception ne doit pas contenir toute logique de l'interface utilisateur ou une entreprise. Considérez ce qui suit, qui va casser votre implémentation actuelle.

public async Task<bool> Login(string username, string password) 
{ 
    var connectionEx = new ConnectionException(); 

    try 
    { 
     ... 
    } 
    catch (Exception ex) 
    { 
     throw connectionEx; 
    } 

    ... 
} 

Maintenant votre utilisateur verra l'exception même s'il n'y en avait pas.

La dernière chose est que je recommande de ne pas attraper l'exception juste pour lancer votre exception personnalisée. La raison en est, qu'il pourrait y avoir d'autres choses qui soulèvent une exception aussi. Par exemple, quelque chose est nul dans la gestion de la réponse positive.

Selon la façon dont la méthode de connexion est utilisé, par exemple directement dans une activité Android, je ferais quelque chose comme ça:

public async Task Login(string username, string password) 
{ 
    //TODO: Do parameter check for username and password 

    try 
    { 
     var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/")); 

     if (response.IsSuccessStatusCode) 
     { 
      // Process the positive response here 
     } 
     else 
     { 
      var alertDialog = new AlertDialog.Builder(context) 
       .SetTitle("Failure") 
       .SetMessage("Request failed.") 
       .SetPositiveButton("OK", (senderAlert, args) => 
        { 
         Finish(); 
        }) 
       .Create(); 

      alertDialog.Show(); 
     } 
    } 
    catch (Exception ex) 
    { 
     var alertDialog = new AlertDialog.Builder(context) 
      .SetTitle("Failure") 
      .SetMessage("Something went wrong (" + ex.Message +")") 
      .SetPositiveButton("OK", (senderAlert, args) => 
       { 
        Finish(); 
       }) 
      .Create(); 

     alertDialog.Show(); 
    } 
} 
+0

Merci pour la réponse. 1) Je ne programme pas normalement en C#, c'est pourquoi je n'ai pas suivi la convention mentionnée, donc maintenant il est inutile de changer toutes les méthodes dans mon code juste pour les démarrer avec une lettre majuscule. 2) Votre suggestion est juste, je ne devrais pas utiliser string.Empty ici. 3) Le but d'une chaîne en tant que valeur de retour est de pouvoir renvoyer un message spécifié à la fonction appelante. Je n'ai pas conçu et implémenté l'API serveur, c'est pourquoi je voulais que la méthode soit suffisamment flexible pour gérer les "messages". Merci encore pour la réponse. – jkwi

+0

J'ai testé votre code et le AlertDialog n'est pas affiché. Au lieu de cela, une autre exception est levée après Show(). – jkwi

+0

Ok, mon mauvais, c'était mon autre partie du code qui a jeté l'exception. J'ai utilisé la solution de @SushiHangover, car la vôtre ne fonctionnerait pas en raison de la création d'AlertDialog sur un thread d'arrière-plan de la méthode async. – jkwi