web-dev-qa-db-ja.com

Android SDK AsyncTask doInBackground not running(サブクラス)

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でアプリを実行しています。

EDIT:

@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()は、インターフェイスParseListenerのコールバックによって呼び出されます。ただし、doInBackground()でlog.v以外は何もしないので、最初にこれを起動することも不要であることが最初にわかりました。

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

EDIT 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);
}
90
SeruK

Matthieuのソリューションはほとんどの場合うまく機能しますが、問題に直面するものもあります。 AndersGöransson の説明のように、ここまたはWebから提供される多くのリンクを掘らない限り。 executeOnExecutorがまだ単一スレッドで動作している場合、ここで他のいくつかの読み取りを要約し、解決策をすばやく説明しようとしています...

AsyncTask().execute();の動作は、Androidバージョン。BeforeDonut(Android:1.6 API:4)タスクは、ドーナツからGingerbreadまで連続して実行されました(Android:2.3 API:9)並列実行されるタスク;Honeycomb(Android:3.0 API:11)実行は順次に切り替えられましたが、新しいメソッドAsyncTask().executeOnExecutor(Executor)が並列実行用に追加されました。

順次処理では、すべての非同期タスクは単一のスレッドで実行されるため、前のタスクが終了するまで待機する必要があります。コードをすぐに実行する必要がある場合は、タスクを個別のスレッドで並列処理する必要があります。

AsyncTaskでは、ドーナツバージョンとハニカムバージョンの間でシリアル実行は使用できませんが、ドーナツの前はパラレル実行は使用できません。

ドーナツ後の並列処理の場合:ビルドバージョンを確認し、それに基づいて.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:_関数.executeOnExecutor()は、プロジェクトのtargetSdkVersionが_HONEYCOMB_MR1_(Android:2.1 API:7)以下であるかどうかをチェックし、エグゼキューターを強制的に_THREAD_POOL_EXECUTOR_(ハニカム後のタスクを順番に実行します)。
targetSdkVersionを定義していない場合、minSdkVersionは自動的にtargetSdkVersionと見なされます。
したがって、ハニカム後のAsyncTaskを並行して実行するため、targetSdkVersionを空のままにすることはできません。

106
Nashe

この答えをチェックアウトする必要があります: 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);
159
Matthieu

これは、次の2つの方法で実行できます。

ウェイ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();
  }

way 1の場合はうまくいきませんway 2を試してください。

ウェイ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);

これがお役に立てば幸いです。

8
Hiren Patel

私は同じ問題を抱えていました:最初の「execute」を呼び出した後、2番目のAsyncTaskを実行できません:doInBackgroundは最初の1つに対してのみ呼び出されます。

これが起こる理由に答えるには、これを確認してください answer (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);
}

更新に関する質問: docs で説明されています。基本的には、インターリファレンス...などのマルチスレッドから生じる可能性のあるすべての問題を回避するためです。

6
code7amza

私が知りたいこと、そしてそれが実際にあなたの問題を解決するかもしれないことの1つは、クラスのインスタンスをインスタンス化し、execute()メソッドを呼び出す場所です? AsyncTaskのドキュメントを読む場合、これらの操作は両方ともメインUIスレッドで実行する必要があります。オブジェクトを作成し、他のスレッドからexecuteを呼び出す場合、onPreExecuteが起動する可能性があります。ここでは100%確実ではありませんが、バックグラウンドスレッドは作成および実行されません。

バックグラウンドスレッドからAsyncTaskのインスタンスを作成する場合、またはメインUIスレッドで実行されない他の操作を行う場合、次のメソッドの使用を検討できます。 Activity.runOnUiThread(Runnable)

そのメソッドを呼び出すには、実行中のアクティビティのインスタンスにアクセスする必要がありますが、UIスレッドで実行されていない他のコードからUIスレッドでコードを実行できます。

それが理にかなっていることを願っています。さらにサポートできるかどうか教えてください。

デビッド

Androidは残忍です!私はこれを信じられません、日から今日までどのようなフレークな実装が変わるか。ある日は単一のスレッド、次はその5、もう1つは128です。

とにかくここでは、在庫のAsyncTaskに代わるものがほとんどありません。必要に応じてAsyncTaskと呼ぶこともできますが、混乱を避けるためにThreadedAsyncTaskと呼ばれます。 execute()はfinalであるため、executeではなくexecuteStart()を呼び出す必要があります。

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

私はこれがスレッドにとって本当に遅いかもしれないことを知っていますが、後で動作しない理由がありますAndroidエミュレーター。非同期タスクが導入されたときAndroid一度に1つだけ実行し、その後、いつか、どのバージョンかわからないため、複数の非同期タスクを一度に実行できるため、多くのアプリで問題が発生したため、Honeycomb +では1つの非同期タスクのみを実行できるようになりましたスレッドプールを手動で変更しない限り、1つまたは2つのことをクリアできることを願っています。

Matthieuの答えに基づき、アプリケーションのコードの重複を避けるために、SDKバージョンに応じてAsyncTaskを正しく実行するhelperクラスの下にあります。

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);
0
L. G.

私はそのSDKだと思います。私は同じ問題を抱えていました。ターゲットSDKを15から11に変更した後、すべてが完全に機能します。

sdk15では、AsyncTask.StatusがRUNNINGであっても、doInBackgroundは呼び出されません。しかし、それはUIスレッドと関係があると思います。

0
phobus