【问题标题】:Show AlertDialog on HttpClient exception (Xamarin Android)在 HttpClient 异常上显示 AlertDialog (Xamarin Android)
【发布时间】:2017-08-28 20:47:02
【问题描述】:

我的 Xamarin Android 应用使用 Web 服务,它使用 HttpClient 连接到该服务。在没有连接时(例如,当用户没有手机或 WiFi 连接时),会抛出异常。我正在使用 async/await 从服务器获取数据。这是我的代码的摘录:

    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;
    }

我定义了一个自定义 ConnectionException 并希望向用户显示一个 AlertDialog 以通知他们请求由于没有连接而失败。用户单击确定后,我想关闭该应用程序。我尝试通过以下方式显示警报对话框,但它不起作用:

    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)
    { }
}

这是正确的方法吗?可能不会,因为它不起作用。对于如何实现这一目标,我将不胜感激。另外,我没有考虑太多,但这是处理此类异常的首选方式吗?

【问题讨论】:

  • 异常对象本身不应该负责显示消息。您还(可能)尝试在后台线程上显示 UI 警报。

标签: c# android xamarin xamarin.android dotnet-httpclient


【解决方案1】:

假设您的myApp.ContextActivity 并且它没有返回堆栈,您可以调用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();
});

【讨论】:

    【解决方案2】:

    您是在多个地方重复使用此异常,还是仅此一次? 如果您只使用一次此异常,则没有真正的理由构建您自己的异常。 您也可以只捕获异常并从您的捕获中发布您的警报。

    我知道这不是编写 catch 的好方法,但如果它有效,为什么不使用它。

    旁注: DisplayAlert 对您来说也可能更容易。这将是一个班轮。

    例子:

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

    【讨论】:

    • DisplayAlert 是 XF,他只是在做 Android
    • 我知道。我不确定他们是否已经在应用程序中使用 XF。如果是,只需使用 DisplayAlert,否则,他们可能会忽略该部分。
    【解决方案3】:

    您处理可能出现的错误的方式包含多个问题,并且由于多种原因不是正确的方法。

    首先:您的代码不遵循 C-Sharp 约定并且包含一些代码异味。我向你展示了一种更好、更被接受的风格。

    1) C# 中的方法通常以大写字母开头。 doLogin 变为 Login

    2) 要创建一个新的 Uri 实例,您不需要格式化您的 url 字符串。不会使用 string.Empty。所以代码可以简化成await GetAsync(new Uri(...));

    3) return_string 似乎没有在方法之外以任何方式使用。它是 string.Empty 或“成功”。为什么不将其切换为布尔值?这样您就可以轻松检查登录是否成功。返回类型变为 bool 而不是 string。

    该方法现在看起来像这样:

    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;
    }
    

    其次,正如@Jason 所提到的,异常不应包含任何 UI 或业务逻辑。请考虑以下内容,这将破坏您当前的实现。

    public async Task<bool> Login(string username, string password)
    {
       var connectionEx = new ConnectionException();
    
        try
        {
           ...
        }
        catch (Exception ex)
        {
            throw connectionEx;
        }
    
        ...
    }
    

    现在您的用户会看到异常,即使没有异常。

    最后一件事是,我建议不要仅仅为了抛出自定义异常而捕获异常。原因是,可能还有其他事情也会引发异常。例如,肯定响应处理中的某些内容为空。

    根据 Login 方法的使用方式,例如直接在 Android Activity 中,我会这样做:

    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();
        }
    }
    

    【讨论】:

    • 感谢您的回答。 1)我通常不使用 C# 编程,这就是为什么我没有遵循上述约定,所以现在改变我的代码中的所有方法只是为了以大写字母开头是没有意义的。 2)你的建议是对的,我不应该在这里使用 string.Empty 。 3)字符串作为返回值的目的是为了能够将指定的消息返回给调用函数。我没有设计和实现服务器 API,这就是为什么我希望该方法足够灵活以处理“消息”。再次感谢您的回答。
    • 我测试了您的代码,但没有显示 AlertDialog。相反,在 Show() 之后引发了另一个异常。
    • 好吧,我的错,是我的代码的另一部分引发了异常。我使用了@SushiHangover 的解决方案,因为 AlertDialog 是在异步方法的后台线程上创建的,因此您的解决方案无法工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 2017-03-02
    相关资源
    最近更新 更多