Android SDK AsyncTask doInBackground未运行(子类)


90

截至2012年2月15日,我还没有找到一个很好的解释,也没有找到不起作用的原因。最接近解决方案的是使用传统的Thread方法,但是为什么要包含一个(似乎)在Android SDK中不起作用的类?

就这样!

我有一个AsyncTask子类:

// ParseListener had a callback which was called when an item was parsed in a
// RSS-xml, but as stated further down it is not used at all right now.
private class xmlAsync extends AsyncTask<String, RSSItem, Void> implements ParseListener

这样执行:

xmlAsync xmlThread = new xmlAsync();

xmlThread.execute("http://www.nothing.com");

现在,该子类遇到了一个小错误。以前它进行了一些xml解析,但是当我注意到它没有被调用doInBackground()时,我逐行将其剥离了下来,最后仅是这样:

@Override
protected Void doInBackground(String... params) 
{
    Log.v(TAG, "doInBackground");
        return null;
}

由于某种原因,它什么也没记录。但是,我添加了以下内容:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

并且在执行线程时确实记录了该行。因此,以某种方式调用了onPreExecute(),但没有调用doInBackground()。我在后台同时运行另一个AsyncTask,效果很好。

我目前正在北极附近的模拟器,SDK版本15,Eclipse,Mac OS X 10.7.2上运行该应用程序。

编辑:

@Override
    protected void onProgressUpdate(RSSItem... values) {

        if(values[0] == null)
        {
                            // activity function which merely creates a dialog
            showInputError();
        }
        else
        {

            Log.v(TAG, "adding "+values[0].toString());
            _tableManager.addRSSItem(values[0]);
        }


        super.onProgressUpdate(values);
    }

_tableManager.addRSSItem()或多或少将行添加到SQLiteDatabase中,并使用活动的上下文进行了初始化。接口ParseListener的回调调用publishProgress()。但是,由于除了doInBackground()中的log.v之外,我什至没有做其他任何事情,所以我首先发现这甚至不需要启动。

编辑2:

好了,很清楚地说,这是另一个AsyncTask,它在同一活动中执行并且工作得很好。

private class dbAsync extends AsyncTask<Void, RSSItem, Void>
{
    Integer prevCount;
    boolean run;

    @Override
    protected void onPreExecute() {
        run = true;
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        run = true;
        prevCount = 0;

        while(run)
        {
            ArrayList<RSSItem> items = _tableManager.getAllItems();

            if(items != null)
            {
                if(items.size() > prevCount)
                {
                    Log.v("db Thread", "Found new item(s)!");
                    prevCount = items.size();

                    RSSItem[] itemsArray = new RSSItem[items.size()];

                    publishProgress(items.toArray(itemsArray));
                }
            }               

            SystemClock.sleep(5000);
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(RSSItem... values) {

        ArrayList<RSSItem> list = new ArrayList<RSSItem>();

        for(int i = 0; i < values.length; i++)
        {
            list.add(i, values[i]);
        }

        setItemsAndUpdateList(list);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onCancelled() {
        run = false;

        super.onCancelled();
    }
}

编辑3:

igh,对不起,我不好意思问问题。但是这里是任务的初始化。

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.execute("http://www.nothing.com", null);
}

活动可能在任务完成之前完成了吗?
Paul Nikonowicz

不大可能。在这种情况下,我的其他线程将无法正常运行?我现在只有一项活动。
SeruK

2
我的应用程序有同样的问题-doInBackground不被调用或被延迟很长时间。这是我的有限观察结果:完全相同的代码在Android 2.3.3智能手机和Android 2.3.3模拟器上都可以完美工作,但在Android 4.0.3平板电脑和许多Android 4.xx模拟器上都存在此问题。很容易得出结论,这个问题是在较新版本的Android中引入的。

抱歉,但是我忘了提到此问题仅在Activity的第二个AsyncTask中发生。第一个AsyncTask总是可以正常工作。

洪,您尝试过Matthieu的答案吗?我主要不在Android游戏ATM上,并且已经有一段时间没有使用它了,所以我无法确定他的答案是否真的有效。如果不适合您,那么我接受他的回答可能对我来说很不好……
SeruK

Answers:


107

Matthieu的解决方案在大多数情况下都可以正常工作,但是有些解决方案可能会遇到问题。除非挖掘此处或从Web提供的许多链接,例如AndersGöransson的解释。我试图在这里总结其他一些读物,并快速解释解决方案,如果executeOnExecutor仍在单线程中工作...

的行为AsyncTask().execute();已通过Android版本更改。在连续执行Donut (Android:1.6 API:4)任务之前,从DonutGingerbread (Android:2.3 API:9)任务并行执行;由于Honeycomb (Android:3.0 API:11)的执行已切换回顺序执行;AsyncTask().executeOnExecutor(Executor)但是,为并行执行添加了新方法。

在顺序处理中,所有异步任务都在单个线程中运行,因此必须等待之前的任务结束。如果需要立即执行代码,则需要在单独的线程中并行处理任务。

使用AsyncTask时,在Donut和Honeycomb版本之间不能执行串行执行,而在Donut之前不能执行并行执行。

对于Donut之后的并行处理:检查Build版本,并基于该版本使用.execute()或.executeOnExecutor()方法。以下代码可以帮助...

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... }; // ... your AsyncTask code goes here
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
    myTask.execute();

NOTE:Function .executeOnExecutor()会检查targetSdkVersion项目的小于或等于HONEYCOMB_MR1(Android:2.1 API:7),然后强制执行程序为THREAD_POOL_EXECUTOR(在Honeycomb之后按顺序运行Tasks)。
如果尚未定义,targetSdkVersion则将minSdkVersion自动视为targetSdkVersion
因此,要在Honeycomb上并行运行AsyncTask,您不能targetSdkVersion留空。


1
很好的答案。尽管Matthieu没错,但我接受了,因为您添加了许多重要信息。
SeruK,2014年

@Nashe非常感谢。真的非常有帮助。我为同一天而奋斗了3天。再次感谢:)

拯救了我的一天!希望这个答案更容易找到。
zjk 2014年

4
嘿@Nashe,我的问题有点尴尬。在今天之前,我在AsyncTask上使用.execute()方法,并且代码运行正常。但是今天我遇到了问题-控件没有进入doInBackground()方法。尽管您提供的解决方案正在运行,但令我感到困惑的是,没有解决方案之前它是如何工作的。我之前和现在使用的是同一套设备。
拉维·索索迪亚

为什么要这样呢?为什么它不能按预期工作?:(
Nikolay R

160

您应该查看以下答案:https : //stackoverflow.com/a/10406894/347565及其包含的Google组的链接。

我有一个与您类似的问题,仍然不清楚为什么它不起作用,但是我像这样更改了代码,问题消失了:

ASyncTask<Void,Void,Void> my_task = new ASyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    my_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
else
    my_task.execute((Void[])null);

我一直很难回顾这篇文章。从那以后我就离开了这个项目。因为人们似乎说这是可行的,所以我只接受这作为答案。
SeruK

这对我现在还是有效的,但是我希望我理解为什么。它在执行之前运行良好,然后停止了。我正在做的一件事是在同一个asynctask的onPostExecute内启动一个新的asynctask(即我递归地调用它)也许与问题有关?
2013年

我认为这应该为您提供所需的所有解释:commonsware.com/blog/2012/04/20/…–
Matthieu

1
@Matthieu:先生,我真的很感谢你!!!我已经在这个问题上动脑筋了好几个小时,您的解决方案使一切正常运行!非常感谢您的精彩回答。我希望我可以给一个以上的支持!!
Swayam


9

您可以通过两种方式执行此操作:

方式1

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) // Above Api Level 13
  {
      asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
else // Below Api Level 13
  {
      asyncTask.execute();
  }

如果方法1对您不起作用,请尝试方法2

方式二

int mCorePoolSize = 60;
int mMaximumPoolSize = 80;
int mKeepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(mCustomThreadPoolExecutor);

希望这会帮助你。


超级花花公子的工作,但接下来使用AsyncTask.THREAD_POOL_EXECUTOR的后续异步任务都失败了,所以我需要在整个项目中使用CustomExecutor进行更改,我想:(
kumar

@vinu,我建议您使用常见的异步任务和常用方法来执行AsyncTask。希望这会帮助你。
埃伦·帕特尔

2
嘿,这对我有很大帮助!谢谢!
贾斯汀·艾比

6

我有同样的问题:在第一个调用“ execute”之后,无法执行第二个AsyncTask:doInBackground仅针对第一个调用。

要回答发生这种情况的原因,请检查此答案(不同的行为取决于SDK)

但是,对于您的情况,可以使用executeOnExecutor(从3.0开始使用4.0.3为我工作)可以避免此障碍,但要注意线程池大小和排队的限制。

你可以尝试这样的事情:

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.executeOnExecutor(_dbLookup.THREAD_POOL_EXECUTOR
 ,"http://www.nothing.com", null);
}

对于您的更新问题: 基本上在文档中进行了解释,以避免所有可能来自多线程的问题,例如intereference...。


5

我想知道的一件事(实际上可能会解决您的问题)是,您在哪里实例化类的实例并调用execute()方法?如果您阅读AsyncTask的文档,则这两个操作都需要在主UI线程上进行。如果您正在创建对象并从其他线程调用执行,那么onPreExecute可能会触发,我在这里不确定100%,但是不会创建和执行后台线程。

如果要从后台线程创建AsyncTask的实例,或者在主UI线程上未执行某些其他操作,则可以考虑使用以下方法: Activity.runOnUiThread(Runnable)

您将需要访问正在运行的Activity的实例才能调用该方法,但是它将允许您从UI线程上未运行的其他代码中在UI线程上运行代码。

希望有道理。让我知道我是否可以提供更多帮助。

大卫


除了这个问题之外,这个线程还有一个有趣的答案stackoverflow.com/questions/4080808/…
manjusg 2012年

感谢您的出色回答!我提高了这一点,因为我认为这可能是AsyncTask初学者的常见问题。,这不是此问题的正确答案。这两个类都在主UI线程上运行的活动的onCreate()中实例化。我在这个项目中只有一个活动。
SeruK

@manjusg我一直以来都认为它与AsyncTask的不稳定有关,当同时运行多个异步信息时,可能更多。如果是这样,为什么?
SeruK

我真的不知道SO连续三遍快速发布有什么政策,但是我在另一个线程中发现了这一点... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html “ AsyncTask使用静态内部工作队列,硬编码限制为10个元素。” 这可能证明了一点,但是我只有两个AsyncTask子类实例!我真的很想避免使用常规的线程方法,因为最终将在dere中完成很多解析。
SeruK '02

2

Android很残酷!我真不敢相信,从今天到今天,什么样的flake实现会发生变化。一天是一个单线程,第二天是5,另一个是128。

无论如何,这里几乎替代了库存AsyncTask。如果愿意,您甚至可以将其称为AsyncTask,但为避免混淆,将其称为ThreadedAsyncTask。您需要调用executeStart()而不是execute,因为execute()是最终的。

/**
 * @author Kevin Kowalewski
 *
 */
public abstract class ThreadedAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 
    public AsyncTask<Params, Progress, Result> executeStart(Params... params){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            return executePostHoneycomb(params);
        }else{
            return super.execute(params);
        }
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private AsyncTask<Params, Progress, Result> executePostHoneycomb(Params... params){
        return super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
    }
}

等等,您并不是说某些版本的Android将异步操作限制为一个线程吗?那真是太傻了。(我离开Android游戏已有一段时间了。:))
SeruK 2013年

是的,在Android 3.0及更高版本上,如果您不使用AsyncTask.THREAD_POOL_EXECUTOR,则只会获得一个线程池。自己尝试一下,尝试两个AsyncTask,然后在doInBackground中入睡。来自Android AsyncTask文档:“从HONEYCOMB开始,在单个线程上执行任务,以避免并行执行引起的常见应用程序错误。”
凯文·帕克

1

我知道这对于线程来说可能真的很晚,但是有一个原因为什么它在以后的Android模拟器上不起作用。当引入asynctask时,android只允许您一次运行一个,然后在某个时候,我不确定是哪个版本,他们允许您一次运行多个asynctask,这导致了很多应用程序出现问题,因此在Honeycomb +中,它们还原为仅一次允许一个异步任务运行。除非您手动更改线程池。希望能为人们解决一两个问题。


0

我认为它的SDK。我有同样的问题,并且将目标sdk从15更改为11后,一切正常。

使用sdk15,即使AsyncTask.Status正在运行,也不会调用doInBackground。我确实认为这与ui线程有关。


我无法否认或确认,因为我现在真的没有时间进行测试。我只能说我正在使用SDK 15,因此很有可能。
SeruK,2012年

0

根据Matthieu的回答,在帮助器类下面可以AsyncTask根据SDK版本正确执行您的操作,以避免在您的应用程序中重复代码:

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

public class AsyncTaskExecutor<Params, Progress, Result> {

  @SuppressLint("NewApi")
  public AsyncTask<Params, Progress, Result> execute(final AsyncTask<Params, Progress, Result> asyncTask, final Params... params){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
      return asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }  else{
      return asyncTask.execute(params);
    }
  }

}

使用示例:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.