后台不运行(子类)

截至2012年15月2日,我还没有找到一个很好的解释,也没有找到一个理由为什么这不起作用。最接近解决方案的方法是使用传统的 线头方法,但是为什么要包含一个在 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 解析,但是当我注意到它的 背景()没有被调用时,我把它一行一行地拆开,最后得到的结果是这样的:

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

出于某种原因,没有任何记录,但是,我添加了这个:

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

在执行线程时,这一行确实会被记录下来。因此,onPreExecute ()被调用了,但是却没有被 doInBack ()调用.我有另一个同时在后台运行的 AsyncTask,它工作得很好。

我目前正在一个模拟器上运行这个应用程序,SDK Version 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 添加了一行,该行是用活动的上下文初始化的。PublishProgress ()由 Interface ParseListener 的回调调用。但是,因为我除了 doInBack ()中的 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:

唉,对不起,我不善于问问题。但是这里是任务的初始化。

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);
}
71212 次浏览

One thing that I would like to know, and it might actually fix your issue, is where are you instantiating the instance of your class and calling the execute() method? If you read the documentation for AsyncTask, both of those operations need to take place on the main UI thread. If you are creating your object and calling execute from some other thread, then onPreExecute might fire, I'm not 100% certain here, but the background thread won't be created and executed.

If you are creating the instance of your AsyncTask from a background thread, or some other operation not taking place on the main UI thread, you could consider using the method: Activity.runOnUiThread(Runnable)

You would need access to an instance of your running Activity to call that method, but it will allow you to run code on the UI thread from some other code that isn't running on the UI thread.

Hope that makes sense. Let me know if I can help more.

David

i think its the sdk. i had the same problem, and after changing target sdk from 15 to 11, everything works perfectly.

with sdk15, even though the AsyncTask.Status is RUNNING, the doInBackground is never called. i do think it has something to do with the ui thread though.

I had the same issue : can't a execute a second AsyncTask after i called "execute" on a first one : doInBackground is only called for the first one.

To answer why this happens check this answer (different behavior depending on the SDK)

However, for your case, this obstacle can be avoided using executeOnExecutor (available starting from 3.0 worked for me using 4.0.3 ) but beware of limitations of the Thread pool size and queuing.

Can you try something like this :

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);
}

For your update question : it is explained in the docs Basically just to avoid all problems that may come from multithreading like intereference ....

You should checkout this answer: https://stackoverflow.com/a/10406894/347565 and the link to google groups it includes.

I had a similar problem as you, still unclear why it is not working, but I changed my code like this and problem is gone:

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);

I know this may be really late for the thread, but there is a reason why it won't work on later android emulators. When asynctask was introduced android only let you run one at a time, then sometime later, im not sure which version, they allowed you to run multiple asynctasks at once, this caused issues in alot of apps,and so in Honeycomb+ they reverted to only allowing one asynctask to run at a time. Unless you manually change the thread pool. Hope that clears one or two things up for people.

Android is Brutal! I can't believe this, what flakey implementation that changes from day to today. One day its a single thread, the next its 5 the other is 128.

Anyways here is a nearly drop in replacement for the stock AsyncTask. You can even call it AsyncTask if you wanted to, but to avoid confusion its called ThreadedAsyncTask. You need to call executeStart() instead of execute because execute() is final.

/**
* @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);
}
}

Based on Matthieu's answer, below an helper class to execute your AsyncTask correctly depending of the SDK version in order to avoid to duplicate code in your application:

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);
}
}


}

Example of use:

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

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);

Matthieu's solution will work fine for most, but some can face problem; unless digging in many links provided here or from web, like Anders Göransson's explanation. I am trying to summarize some other reads right here and quickly explain solution if executeOnExecutor is still working in single thread...

Behavior of AsyncTask().execute(); has changed through Android versions. Before Donut (Android:1.6 API:4) tasks were executed serially, from Donut to Gingerbread (Android:2.3 API:9) tasks executed paralleled; since Honeycomb (Android:3.0 API:11) execution was switched back to sequential; a new method AsyncTask().executeOnExecutor(Executor) however, was added for parallel execution.

In sequential processing all Async tasks run in a single thread and thus have to wait before the previous task ends. If you need to execute code immediately, you need tasks to be processed in parallel in separate threads.

With AsyncTask serial execution is not available between Donut and Honeycomb versions, while parallel execution is not available before Donut.

For parallel processing after Donut: Check the Build version and based on that use .execute() or .executeOnExecutor() method. Following code can help...

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() has checks if targetSdkVersion of project is less than or equal to HONEYCOMB_MR1 (Android:2.1 API:7) then it forces the executor to be THREAD_POOL_EXECUTOR (which runs Tasks sequentially in post Honeycomb).
If you have not defined a targetSdkVersion then minSdkVersion is automatically considered to be the targetSdkVersion.
Hence for running your AsyncTask in parallel on post Honeycomb you cannot leave targetSdkVersion empty.

You can do this by two ways:

Way 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();
}

In case of way 1 not works for you try way 2.

Way 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);

Hope this will help you.