使用Android AsyncTask下载HTML文件

4

我刚开始学习安卓,并且正在开发一个简单的应用程序,应该下载HTML文件的内容。像建议的那样,我使用AsyncTask,但我遇到了一个问题。在下面的代码中(我遵循了教程代码),我在onPostExecute方法中得到了tv不能被解决的错误。如何访问已下载的文件?谢谢:

public class FlashResults extends Activity {
  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        setContentView(tv);
        readWebpage(tv);                
  }


  protected class DownloadPage extends AsyncTask<String, Void, String> {
          protected String doInBackground(String... urls) {

          String responseStr = null;

          try {
              for (String url : urls) {   
              DefaultHttpClient httpClient = new DefaultHttpClient();
              HttpGet get = new HttpGet(url);
              HttpResponse httpResponse = httpClient.execute(get);
              HttpEntity httpEntity = httpResponse.getEntity();
              responseStr = EntityUtils.toString(httpEntity);
              } 
          } catch (UnsupportedEncodingException e) {

          } catch (ClientProtocolException e) {

          } catch (IOException e) {

          }
          return responseStr;
      }

      protected void onPostExecute(String result) {           
          tv.setText(result);
      }
  }

  public void readWebpage(View v) {
        DownloadPage task = new DownloadPage();
        task.execute(new String[] { "http://seznam.cz" });
      }

}


1
上下文问题。tv是在onCreate中声明的局部变量(因此只能从那里访问)。将其变为实例字段。 - njzk2
5个回答

4
到目前为止,所有其他的答案都是可行的。不过,我还想补充几点:
  1. 如果你只在这个 onCreate 和 DownloadPage 活动中访问 TextView tv,那么你可以直接将它分配给 DownloadPage 的构造函数,以限制对 tv 的访问。
  2. 对于像 DownloadPage AsyncTask 这样有用的东西,我通常会将其从任何活动的内部类中删除,并将其放入名为“Utils”的公共类中,以便其他许多活动根据需要使用(代码的模块化)。
  3. 如果你要使用内部类(完全合法),最好的做法是将其设为 privatestatic
像这样:
public class FlashResults extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        setContentView(tv);
        readWebpage(tv);
    }

    public void readWebpage(View v) {
        DownloadPage task = new DownloadPage(tv);
        task.execute(new String[] { "http://seznam.cz" });
    }

    private static class DownloadPage extends AsyncTask<String, Void, String> {

        private TextView textView;
        public DownloadPage(TextView tv){
            textView = tv;
        }

        protected String doInBackground(String... urls) {

         String responseStr = null;

          try {
              for (String url : urls) {   
              DefaultHttpClient httpClient = new DefaultHttpClient();
              HttpGet get = new HttpGet(url);
              HttpResponse httpResponse = httpClient.execute(get);
              HttpEntity httpEntity = httpResponse.getEntity();
              responseStr = EntityUtils.toString(httpEntity);
              } 
          } catch (UnsupportedEncodingException e) {

          } catch (ClientProtocolException e) {

          } catch (IOException e) {

          }
            return responseStr;
        }

        protected void onPostExecute(String result) {           
            if (textView != null) {
                textView.setText(result);
            }
        }
    }
}

}


1
为什么要使 AsyncTaskstatic - Squonk
@Squonk 有趣。仔细看了一下,也许它不需要是静态的。那些不明确需要引用外部类的内部类应该保持静态。但是我在这里看到 tv 实例将会保存活动的引用(只是从初始化 tv 开始)。这更多是关于良好编码实践理论的建议,可能并不完全适用于这个特定情况。你说得对。 - David T.
1
把一个小部件的引用传递给其他函数,这不是个好主意吧——如果用户在文件下载时旋转手机……那程序就会崩溃。 - Someone Somewhere
@SomeoneSomewhere 可能需要在 onPostExecute 中添加一个空值检查? - David T.

3

一种方法是按照其他答案建议的那样,将tv设置为实例级别。另一种方法是在AsyncTask中创建一个TextView字段并将引用传递到构造函数中:

...
public void readWebpage(TextView v) {
    DownloadPage task = new DownloadPage(v);
    task.execute(new String[] { "http://seznam.cz" });
}
...
protected class DownloadPage extends AsyncTask<String, Void, String> {
    protected String doInBackground(String... urls) {
        ...
    }
    TextView tv = null;

    public DownloadPage(TextView tv){
        this.tv = tv;
    }
    ...
}

@DavidT. :我不同意。这对于单个TextView可能很好用,但是如果操作者在未来扩展UI并需要访问/操作多个UI元素呢? - Squonk
1
@dave.c:代码有缺陷 - 你将responseStrtv的声明和构造函数嵌入到了doInBackground(...)方法中。 - Squonk
1
@Squonk 啊,好发现,我已经编辑了他的答案以修复你提出的问题。但是问题是 - OP代表什么? - David T.
2
我认为它代表“原始帖子发布者”。如果他想避免实例级引用,比这更好的方法是在AsyncTask中使用findViewById(id)。我从来没有理解为什么人们嵌套类然后传递变量而不是直接访问它们。 - toadzky
1
@dave.c:正如toadzky所说,OP通常在论坛上代表“原始帖子”或“原始发帖人” - 也许这是一个老术语,我只是暴露了我的年龄。 ;) 我也同意toadzky的观点,关于在将AsyncTask作为内部类时传递引用的情况 - 这是完全不必要的,因为内部AsyncTask的所有方法(除了doInBackground(...))都可以完全访问Activity的UI元素。 - Squonk
显示剩余3条评论

1
如果你想让一个变量在方法外可访问,你需要在方法外声明它。这是一个基本的编程概念,叫做作用域。
将你的代码改为以下形式:
public class FlashResults extends Activity {
private TextView tv;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    tv = new TextView(this);
    setContentView(tv);
    readWebpage(tv);                
}

并且它将可用于FlashResults内的任何内容。


1
尝试将文本视图类作为类级变量。
private TextView tv;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    tv = new TextView(this);
    setContentView(tv);
    readWebpage(tv);                
}

另一种选择是将您的AsyncTask声明为匿名的,在与任务声明相同的方法体中将TextView声明为final。 干杯

0

你必须将tv作为一个字段,以便从子类中访问它。

    public class FlashResults extends Activity {

      TextView tv;

      @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            tv = new TextView(this);
            setContentView(tv);
            readWebpage(tv);                
      }


      protected class DownloadPage extends AsyncTask<String, Void, String> {
              protected String doInBackground(String... urls) {

              String responseStr = null;

              try {
                  for (String url : urls) {   
                  DefaultHttpClient httpClient = new DefaultHttpClient();
                  HttpGet get = new HttpGet(url);
                  HttpResponse httpResponse = httpClient.execute(get);
                  HttpEntity httpEntity = httpResponse.getEntity();
                  responseStr = EntityUtils.toString(httpEntity);
                  } 
              } catch (UnsupportedEncodingException e) {

              } catch (ClientProtocolException e) {

              } catch (IOException e) {

              }
              return responseStr;
          }

          protected void onPostExecute(String result) {           
              tv.setText(result);
          }
      }

      public void readWebpage(View v) {
            DownloadPage task = new DownloadPage();
            task.execute(new String[] { "http://seznam.cz" });
          }
}

这不会引起垃圾回收问题吗?如果用户不断改变设备的方向,实例变量没有被释放怎么办? - Ralph Yozzo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接