【问题标题】:How to pass variables in and out of AsyncTasks?如何将变量传入和传出 AsyncTasks?
【发布时间】:2012-03-28 03:41:25
【问题描述】:

我没有花太多时间在 Android 中使用 AsyncTasks。我试图了解如何将变量传入和传出类。语法:

class MyTask extends AsyncTask<String, Void, Bitmap>{

     // Your Async code will be here

}

它与类定义末尾的&lt; &gt; 语法有点混淆。以前从未见过这种类型的语法。似乎我仅限于将一个值传递给AsyncTask。我假设这个不正确吗?如果我还有更多要通过,我该怎么做?

另外,如何从 AsyncTask 返回值?

这是一个类,当你想使用它时,你调用new MyTask().execute(),但你在类中使用的实际方法是doInBackground()。那么你实际上在哪里返回一些东西呢?

【问题讨论】:

  • 您可能应该查看一些示例和 android 文档以获得更好的想法。
  • 我有,文档没有说明将多个对象传递给 AsyncTask。
  • 我刚刚意识到,因为它是一个类,我可能只是创建公共成员并将这些成员设置为.execute()之前我想要的任何东西......我认为这会奏效。
  • 好的,如果您对传递多个对象感到困惑,那么您可以查看我的答案。
  • 你真的需要阅读这个教程 - Ultimate Guide - Android AsyncTask Example too

标签: android android-asynctask


【解决方案1】:

注意:Android 开发者 AsyncTask reference page 上提供了以下所有信息。 Usage 标头有一个示例。另请查看 Painless Threading Android Developers Blog Entry

看看the source code for AsynTask


有趣的&lt; &gt; 表示法可让您自定义异步任务。括号用于帮助实现generics in Java

您可以自定义任务的 3 个重要部分:

  1. 传入的参数类型 - 任意数量
  2. 用于更新进度条/指示器的类型
  3. 后台任务完成后返回的类型

请记住,以上任何一个都可能是接口。这就是你可以在同一个调用中传递多种类型的方法!

你把这三个东西的类型放在尖括号里:

<Params, Progress, Result>

因此,如果您要传入URLs 并使用Integers 更新进度并返回一个表示成功的布尔值,您可以这样写:

public MyClass extends AsyncTask<URL, Integer, Boolean> {

在这种情况下,例如,如果您正在下载位图,您将在后台处理您对位图所做的事情。如果需要,您也可以只返回位图的 HashMap。还要记住你使用的成员变量是不受限制的,所以不要被参数、进度和结果束缚。

要启动 AsyncTask 实例化它,然后execute 它可以顺序或并行进行。在执行中是您传递变量的地方。你可以传入多个。

请注意,您不要直接致电doInBackground()。这是因为这样做会破坏 AsyncTask 的魔力,即 doInBackground() 在后台线程中完成。直接按原样调用它会使其在 UI 线程中运行。因此,您应该使用execute() 的形式。 execute() 的工作是在后台线程而不是 UI 线程中启动 doInBackground()

使用上面的示例。

...
myBgTask = new MyClass();
myBgTask.execute(url1, url2, url3, url4);
...

onPostExecute 将在执行的所有任务完成后触发。

myBgTask1 = new MyClass().execute(url1, url2);
myBgTask2 = new MyClass().execute(urlThis, urlThat);

注意如何将多个参数传递给execute(),它将多个参数传递给doInBackground()。这是通过使用varargs(你知道像String.format(...)。很多例子只展示了使用params[0]提取第一个参数,但你应该make sure you get all the params。如果你是传入 URL 将是(取自 AsynTask 示例,有多种方法可以做到这一点):

 // This method is not called directly. 
 // It is fired through the use of execute()
 // It returns the third type in the brackets <...>
 // and it is passed the first type in the brackets <...>
 // and it can use the second type in the brackets <...> to track progress
 protected Long doInBackground(URL... urls) 
 {
         int count = urls.length;
         long totalSize = 0;

         // This will download stuff from each URL passed in
         for (int i = 0; i < count; i++) 
         {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }

         // This will return once when all the URLs for this AsyncTask instance
         // have been downloaded
         return totalSize;
 }

如果您要执行多个 bg 任务,那么您需要考虑上述myBgTask1myBgTask2 调用将按顺序进行。如果一个调用依赖于另一个调用,这很好,但如果调用是独立的 - 例如,您正在下载多个图像,并且您不在乎哪些图像先到达 - 那么您可以进行 myBgTask1myBgTask2 调用与THREAD_POOL_EXECUTOR并行:

myBgTask1 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2);
myBgTask2 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urlThis, urlThat);

注意:

示例

这是一个示例 AsyncTask,它可以在同一个 execute() 命令上采用任意数量的类型。限制是每种类型必须实现相同的接口:

public class BackgroundTask extends AsyncTask<BackgroundTodo, Void, Void>
{
    public static interface BackgroundTodo
    {
        public void run();
    }

    @Override
    protected Void doInBackground(BackgroundTodo... todos)
    {
        for (BackgroundTodo backgroundTodo : todos)
        {
            backgroundTodo.run();

            // This logging is just for fun, to see that they really are different types
            Log.d("BG_TASKS", "Bg task done on type: " + backgroundTodo.getClass().toString());
        }
        return null;
    }
}

现在你可以这样做了:

new BackgroundTask().execute(this1, that1, other1); 

其中每个对象都是不同的类型! (实现相同的接口)

【讨论】:

  • 非常全面,谢谢。虽然我一直在寻找一种将多种类型传递给任务的方法。
  • @Jakobud - 是的,问题是超类型无法实现通配符 (?)。但是,我会做的是为每种类型创建一个 AsyncTask。由于可能会有代码重复,您可以创建一个通用的基类来继承。此外,您可以只传递普通的旧Object 并使用instanceof 进行适当的转换......但是,这可能不是一个好的解决方案,因为您当时正在与Java 作斗争。 - 最后,您可以将参数设置为Void 并改用自定义成员字段。这些可以使用每种类型的不同方法传递给对象。
  • 你能简单地为 AsynTask 创建一个构造函数,它接受你想要的任何参数吗?或者这门课不是真的那样工作吗?我现在正在尝试的似乎工作正常的解决方案是创建公共类成员并将这些成员设置为在创建 AsyncTask 之后但在.execute() 之前。这是一个不好的方法吗?到目前为止它似乎工作得很好......
  • @Jakobud - 是的,你可以随心所欲地制作构造函数。这是为类成员传递值的好地方。您还可以使用 setter 方法来设置这些类成员(因此它们不是公共的 - 这将增加您的代码的灵活性)。它基本上是一个和其他类一样的类。
  • 好吧,这是有道理的。感谢您的帮助!
【解决方案2】:

我知道这是一个迟到的答案,但这是我最近一直在做的事情。

当我需要将一堆数据传递给 AsyncTask 时,我可以创建自己的类,将其传入,然后访问它的属性,如下所示:

public class MyAsyncTask extends AsyncTask<MyClass, Void, Boolean> {

    @Override
    protected Boolean doInBackground(MyClass... params) {

        // Do blah blah with param1 and param2
        MyClass myClass = params[0];

        String param1 = myClass.getParam1();
        String param2 = myClass.getParam2();

        return null;
    }
}

然后像这样访问它:

AsyncTask asyncTask = new MyAsyncTask().execute(new MyClass());

或者我可以向我的 AsyncTask 类添加一个构造函数,如下所示:

public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {

    private String param1;
    private String param2;

    public MyAsyncTask(String param1, String param2) {
        this.param1 = param1;
        this.param2 = param2;
    }

    @Override
    protected Boolean doInBackground(Void... params) {

        // Do blah blah with param1 and param2

        return null;
    }
}

然后像这样访问它:

AsyncTask asyncTask = new MyAsyncTask("String1", "String2").execute();

希望这会有所帮助!

【讨论】:

    【解决方案3】:

    由于您可以在方括号中传递对象数组,因此这是传递您希望在后台进行处理的数据的最佳方式。

    您可以在构造函数中传递您的活动或视图的引用,并使用它将数据传回您的活动

    class DownloadFilesTask extends AsyncTask<URL, Integer, List> {
        private static final String TAG = null;
        private MainActivity mActivity;
        public DownloadFilesTask(MainActivity activity) {
            mActivity = activity;
            mActivity.setProgressBarIndeterminateVisibility(true);
        }
    
        protected List doInBackground(URL... url) {
            List output = Downloader.downloadFile(url[0]);
            return output;
        }
    
        protected void onProgressUpdate(Integer... progress) {
            setProgressPercent(progress[0]);
        }
    
        private void setProgressPercent(final Integer integer) {
            mActivity.setProgress(100*integer);
        }
    
        protected void onPostExecute(List output) {
    
            mActivity.mDetailsFragment.setDataList((ArrayList<Item>) output);
    
            //you could do other processing here
        }
    }
    

    【讨论】:

    • 我想如果我想发送多个相同类型的变量,比如字符串数组,这会起作用。在我的情况下,我需要传递一个自定义对象以及一个参数的 HashMap……我觉得我应该使用 AsyncTask 以外的东西……
    • 你也可以发送一组自定义对象
    【解决方案4】:

    或者,您可以只使用常规线程和使用处理程序通过覆盖 handlemessage 函数将数据发送回 ui 线程。

    【讨论】:

      【解决方案5】:

      传递一个简单的字符串:

       public static void someMethod{ 
           String [] variableString= {"hello"};
           new MyTask().execute(variableString);
      }
      
      static class MyTask extends AsyncTask<String, Integer, String> {
      
              // This is run in a background thread
              @Override
              protected String doInBackground(String... params) {
                  // get the string from params, which is an array
                  final String variableString = params[0];
      
                  Log.e("BACKGROUND", "authtoken: " + variableString);
      
                  return null;
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2019-06-14
        • 2010-10-29
        • 2018-09-24
        • 2019-01-12
        • 1970-01-01
        • 2011-04-16
        • 2014-07-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多