【问题标题】:Checking if url exists检查 url 是否存在
【发布时间】:2015-05-15 21:29:03
【问题描述】:

对于我的项目,我需要检查一个 URL 以查看它是否存在。 所以我创建了一个HttpURLConnection 并连接到 url 并等待响应代码。 由于我不能在我的主线程中使用那个 HttpURLConnection,我不得不将它移动到一个 AsyncTask 并让它在一个单独的线程上运行。

这就是我的 AsyncTask 代码的样子(仅重要部分):

private class URLExists extends AsyncTask<String, Void, Boolean> {

    protected Boolean doInBackground(String... urls)
    {
        boolean isValid = false;

        try
        {
            URL myurl = new URL(urls[0]);
            HttpURLConnection.setFollowRedirects(false);

            HttpURLConnection connection = (HttpURLConnection) myurl.openConnection();
            connection.setUseCaches(true);
            connection.setRequestMethod("HEAD");
            connection.connect();
            int code = connection.getResponseCode();

            ...

            connection.disconnect();

            if(code != 404)
                isValid = true;

            else
                isValid = false;
        }

            ...
        return isValid;
    }

    protected void onPostExecute(Boolean isValid)
    {
        isTrue = isValid;
    }
}

所以基本上我会得到一个布尔值isTrue,它告诉我网址是否有效。当我运行它时,我得到了正确的结果,一切正常。

但是现在,当我执行这个 AsyncTask 时,它显然与我的主线程并行运行。

我有这个代码:

new URLExists().execute("https://www.example.com");
if(isTrue)
{
    myButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {
                //Do Something
            }
        });
}

由于我的 AsyncTask 并行运行,我的主线程执行下一个操作而不等待 AsyncTask 的结果。因为isTrue默认是false,所以if语句永远不会运行。

我当然知道 AsyncTask 中的 protected void onPostExecute(),但这是否意味着我的主线程中的所有代码都必须在并行线程中的那个函数中?这一切都取决于 URL 检查的结果。

我知道 .get() 方法,但这会冻结我的 MainThread 和 UI,所以这对我来说不是一个真正的选择。

还有其他方法可以让主线程等待 AsyncTask 吗?我知道 AsyncTask 的目的是失败的,但我只想检查 URL 是否存在,我只能在 AsyncTask 中进行检查。

【问题讨论】:

  • 不建议等待主线程,并将其视为不良用户体验。您可以将有关设置单击侦听器的代码移动到AsyncTaskonpostexecute。但是,您应该以某种方式让用户知道该按钮将无法按预期工作。我会禁用它,直到我从网络得到答案,或者推迟点击执行,直到我从网络得到答案。更多关于你 asynctask 的 cmets。 404 不仅是一种错误响应代码,而且许多网络服务器也无法正确处理 HEAD 请求。所以请注意这些事实
  • 非常感谢您的回答!

标签: java android android-asynctask httpurlconnection


【解决方案1】:

您的任务不需要多线程。您需要做的就是检查 url 是否可用。正确的?当然这可能需要一些时间,你必须在你的主线程中等待这个时间。这是正确的。但你仍然需要等待。不要创造不必要的复杂性。只需读取 url,获取响应代码,验证它,检查超时异常就可以了。

【讨论】:

  • 怎么样?我以为我需要 AsyncTask 的多线程,因为它不会在主线程上运行?我还能如何检查 URL 是否存在
  • 只要把connection.connect();代码 = 连接.getResponseCode();到您的代码,这已经足够了。但是,这可能会让您感到困惑,您的主线程应该与 UI 线程分开。换句话说 - 不要在 UI 线程中做任何事情(尤其是耗时的数据库连接或 http 连接) - 例如在 onClick 方法中。 onClick 在 GUI 线程中执行。如果放置断点,您可以看到它 - 当您停止时您可以调试,但您的 GUI 是不负责任的。常规练习 - 在 onClick 中启动另一个线程来完成这项工作并尽快返回控制权。
  • 非常感谢您的回答!
【解决方案2】:

我们需要看一下 AsyncTask 的规范。通常在任何异步方法中,您都有一个回调函数,可以让您知道后台操作已完成。对于 Android,它是 onPostExecute()

可以在这里找到。 http://developer.android.com/reference/android/os/AsyncTask.html

当一个异步任务被执行时,任务会经过4个步骤:

  1. onPreExecute(),在执行任务之前在 UI 线程上调用。此步骤通常用于设置任务,例如通过在用户界面中显示进度条。

  2. doInBackground(Params...),在 onPreExecute() 执行完成后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。异步任务的参数传递到这一步。计算的结果必须由这一步返回,并将传递回最后一步。此步骤还可以使用 publishProgress(Progress...) 来发布一个或多个进度单位。这些值在 UI 线程的 onProgressUpdate(Progress...) 步骤中发布。

  3. onProgressUpdate(Progress...),在调用 publishProgress(Progress...) 后在 UI 线程上调用。执行的时间是不确定的。此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。例如,它可用于动画进度条或在文本字段中显示日志。

  4. onPostExecute(Result),在后台计算完成后在 UI 线程上调用。后台计算的结果作为参数传递给这一步。

对于您的具体情况,您可能想做这样的事情..

yourAsyncTask{
   @Override
   protected Boolean doInBackground(Void... params){
        // CHECK YOUR URL HERE
   }
   .
   .
   .
   @Override
   public void onPostExecute(Result result){
      if(isTrue) {
        myButton.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            //Do Something
         }
        });
      }
   }
}

【讨论】:

  • 我知道 onPostExecute 就像我之前在我的问题中所说的那样。问题是在 URL 检查后我有很多代码要执行。我是否必须将整个代码放入该 onPostExecute 函数中?
  • 如果是依赖于响应的代码,那么是的,它将进入 onPostExecute 回调。