从另一个AsyncTask的doInBackground()方法开始执行AsyncTask

9
我试图从另一个AsyncTask的doInBackground()方法中启动一个AsyncTask... 这是可能的吗?如果是,我该如何实现?
后续编辑:
我的想法是启动一个asynctask,它将从Twitter获取我需要的状态... 到这里一切都好... 但当我遇到一些特定的推文时,我需要使用来自我的服务器的一些信息修改它们,在这里我将进行另一个网络操作,并且由于我需要等待直到我从服务器获取信息,因此我会执行:
GetContent getContentServiceAsyncTask = new GetContent(context);
try {
    tweetText = Uri.decode(getContentServiceAsyncTask.execute(
            URL_GET_CONTENT, jsonRequest).get());
} catch (InterruptedException e) {
    Log.e(TAG_DEBUG, "InterruptedException: ", e);
} catch (ExecutionException e) {
    Log.e(TAG_DEBUG, "ExecutionException: ", e);
}

这是从已经开始的AsyncTask中的doInBackground()方法开始的...
我知道我可以只在AsyncTask中添加方法,然后在doInBackground()方法中调用它们,但我需要在其他地方使用它们,在这些地方我从onPostExecute中启动这些AsyncTasks...
如果你们认为有一个简单的解决方法,不会影响我的性能,那就太好了...如果没有,我将创建一些静态方法,我将在所有需要的AsyncTasks中调用它们(但这将需要我修改很多代码)。

可以实现,但如果您尝试从第二个任务的onPostExecute修改UI,它将崩溃,因为它不在主线程中执行。 - Juangcg
1
建议在第一个异步任务的post execute中开始异步操作。 - Subburaj
如果任何给出的答案对您有帮助,请随意接受 :) - Langusten Gustel
1
嗨Negru,你的第一个异步任务必须在并行任务架构上运行。因此,你可以使用executor启动你的第一个任务 ==> "new FirstTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, <params>);" 第二个任务在你的firsttask的doInbackgreound()中运行。我将创建示例应用程序并编写答案。 - nurisezgin
我不需要并行执行,因为正如您所看到的,我在内部AsyncTask中使用get()...此外,执行程序是在API级别11中添加的,这对我来说不好,因为我的应用程序还支持较低的级别...但仍然是一个好答案...谢谢。 - Ionut Negru
显示剩余3条评论
7个回答

15

AsyncTasks 应该在 主线程 (UI) 中运行。你不应该在 doInBackground 方法中启动另一个 AsyncTask,因为这个方法是在非 UI 线程上执行的。

对于你的情况,我可以建议你两件事:

  1. 把你两个 AsyncTaks 的处理合并为单个请求。
  2. 在第一个任务的 onPostExecute 中启动你的第二个 AsyncTask。

2
@downvoter:可以解释一下为什么要点踩吗? - waqaslam
我做不到那个...我需要一个阻塞的AsyncTask,我使用.get()调用...我不能等到当前的AsyncTask完成并在onPostExecute()中进行更新。 - Ionut Negru

11

根据下面的帖子,您可以使用Activity.runOnUiThread()在主线程上运行Runnable(从另一个线程中)。

因此理论上您可以这样做:

  • 运行Async任务
  • 在AsyncTask内部执行Activity.runOnUiThread(Runnable),并从此runnable启动新的AsyncTask

正如名称所说,Activity.runOnUiThread()在主线程上执行runnable。

但这有点hacky。

代码应该看起来像这样:(未经测试)

// first task
    (new AsyncTask<String, String, String>() {

        @Override
        protected String doInBackground(String... params) {
            ParentActitity.this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    //second async stared within a asynctask but on the main thread
                    (new AsyncTask<String, String, String>() {

                        @Override
                        protected String doInBackground(String... params) {
                            // TODO Auto-generated method stub
                            return null;
                        }

                    }).execute();

                }
            });
            return null;
        }

    }).execute();

在生产环境中,这个嵌套的例子并不是一个好的样式,因为(我个人认为)它几乎无法阅读。

附加说明:

Activity.runOnUiThread(Runnable) 不是静态的!这就是为什么我的例子使用 ParentActivity(.this).runOnUiThread(Runnable)。


我该如何运行它?请记住,我在一个像这样的AsyncTask类中: public class DoSomeBackgroundWork extends AsyncTask<String,String,Boolean> {...} - Ionut Negru
谢谢jmeier,但我收到以下警告: “TwitterActivity类型的封闭实例在范围内不可访问” - Ionut Negru
你必须在对你的TwitterActivity实例的引用上调用它。你这样做了吗? - Langusten Gustel
不过最终我认为这样做会带来太多麻烦并影响性能,因此我创建了我需要直接在这个AsyncTask中调用的方法... 不过,我认为你的回答是最接近我所寻找的,所以我会接受它。 谢谢。 - Ionut Negru

6

doInBackground()中不能实现该功能,但是你可以在onProgressUpdate()中实现,因为它运行于UI线程。请记住,自从3.0版本后,它们将被放置在相同的队列中,并且不会并行运行,详见executeOnExecutor


我知道executeOnExecutor的用法,但我需要我的应用程序在所有Android版本(> 8)上保持一致。 - Ionut Negru

0

我写的基本示例;

public class MainActivity extends Activity {

View view;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    view = new View(this);
    setContentView(view);

    view.setBackgroundColor(Color.RED);

    new FirstTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
}

private class FirstTask extends AsyncTask<Void, Void, Void>
{
    @Override
    protected Void doInBackground(Void... params) {

        new SecondTask().execute();
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Log.e("task","first");
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        view.setBackgroundColor(Color.GREEN);   
    }

}

private class SecondTask extends AsyncTask<Void, Void, Void>
{
    @Override
    protected Void doInBackground(Void... params) {
        Log.e("task","second");
        return null;
    }
}

}

正如Penna所说,Executor是在API 11级中添加的...所以这对我来说不是正确的方法。 - Ionut Negru

0

很简单,你需要使用Executor:

new AsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{"param 1", "param 2"});

此方法通常与THREAD_POOL_EXECUTOR一起使用,以允许多个任务在由AsyncTask管理的线程池中并行运行,但您也可以使用自己的Executor进行自定义行为。

警告:从线程池并行运行多个任务通常不是人们想要的,因为它们的操作顺序未定义。例如,如果这些任务用于修改任何共同状态(例如由于按钮单击而写入文件),则无法保证修改的顺序。如果没有仔细的工作,偶尔会出现较新版本的数据被旧版本覆盖的情况,从而导致数据损失和稳定性问题。这样的更改最好是串行执行;为了保证这样的工作无论平台版本如何都是串行化的,可以使用此函数与SERIAL_EXECUTOR。


这是一个非常古老的问题。现在有更好、更简单的解决方案:D - Ionut Negru

0

我知道这一点,这就是为什么我问你们是否知道一些解决方法的原因! - Ionut Negru
1
@lonutNegru 你的问题是“这是否可能”。我的回答是“不可能”(至少不是直接的)。你需要在主线程上发布一个可运行的代码,其中包含执行新AsyncTask的代码。jmeier的答案提供了一种可能的方法来解决这个问题。 - Alex Lockwood
是的,谢谢Alex Lockwood,最终我选择修改结构以避免这个问题,并在未来保持安全 :) ... - Ionut Negru

-1

将这些inet操作捆绑在一起,启动一个单独的线程来处理数据管理的这一部分,这样做会更好吗?谷歌无处不在地使用模式,如果您仔细观察,它们都源自简单的类... 这个API使得Android像一个蜘蛛网回调涉及回调... 尝试理解Java的基础知识,编写一段好代码并不难。服务/内容解析器/接收器/加载器在某种程度上是有限的... 线程几乎可以实现您想要的任何事情...

大多数人在使用代码时没有思考,甚至不尝试了解它的工作原理和编程关键。我尽量不使用我不了解基础的代码。如果我需要使用,我会彻底学习它。我曾经用Java进行黑魔法,现在我认为它是数据流A->B->C等.. 良好的流程是必不可少的

编辑:

是的,我为您的问题编写了解决方案

有很多方法可以实现您想要的东西,但我不会为您编写代码。

  • 你可以使用IntentService类,它有一个内置队列 --- 只需为每个传输使用单独的startService()调用。

  • 你可以使用Thread - 我喜欢的方式

  • 你可以使用Service

  • 你可以使用AsyncTaskLoader

  • 但是如果你需要同时触发两个及以上相互连接的异步任务,请不要使用ASYNC TASK,因为在现代手机上,它们都在单一线程上以队列方式运行(另外,你可以通过在EXECUTOR上运行它们来解决这个问题)

JAVA 1.5版本以后,任务与线程分开了 - 这时加入了

你说的自定义线程是什么意思?

THREAD(我们所说的)不是指THREAD类对象,而是指在THREAD类的run()方法中定义的TASK,因此任何从THREAD派生的CLASS仍然会启动THREAD(也就是TASK)

    public synchronized void start() {
        checkNotStarted();

        hasBeenStarted = true;
        /** 
         * look at this line i'ts rerouting run() code of youre thread class  
         * to native method from there thread is created in pure C code 
         */
        nativeCreate(this, stackSize, daemon);
    }

举个例子,POSIX 中是这样写的:

int  pthread_create(pthread_t  *  thread, pthread_attr_t * attr, void *
   (*start_routine)(void *), void * arg);

从这个点开始,我们可以谈论多任务处理,当操作系统在一定时间内通过并发执行多个任务来执行它们时。

自从JAVA 1.5以来,有一个Executor接口,它提供了一种将任务提交与如何运行每个任务的机制(包括线程使用、调度等细节)分离的方法。通常情况下,Executor被用来代替显式创建线程。你有很多执行器之一是:ThreadPerTaskExecutor。

class ThreadPerTaskExecutor implements Executor {
      public void execute(Runnable r) {
          new Thread(r).start();
      }
  }

This executor spawns a new thread for each task.

回到AsyncTask:

AsyncTaskM<P, P, R>.executeOnExecutor(Executor,P);

allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask, however you can also use your own Executor for custombehavior.

如果你感觉力不从心,或者不够了解,最好让其他人来做吧 :)

我希望这个补充能改变你对投票的看法。


你的回答没有提供任何实现方法,这就是我为什么会点踩的原因。有更好的方法可以实现,比如接口、自定义线程、广播等等,但在我发布问题时并不知道很多。我还推荐使用EventBus来解耦组件。 - Ionut Negru

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