12

私が取り組んでいるアプリケーションは、バックグラウンド スレッドを使用して API を介して画像のリストをダウンロードし、画像をスライドショーで表示します。

新しい画像を定期的に取得するためのバックグラウンド タスク (現在は AsyncTask) があります。

間違ったスレッドなどに関するエラー メッセージは表示されません。AsyncTasks の 2 番目のインスタンスが doInBackground メソッドを実行しないだけです。

アクティビティのコードを次に示します。

private DownloadTask mDownloadTask = null;
private Handler mHandler;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(mDownloadTask != null) {
                mDownloadTask.cancel(true);
            }
            mDownloadTask = new DownloadTask();
            mDownloadTask.execute((Void[]) null);
        }
    };

    mDownloadTask = new DownloadTask();
    mDownloadTask.execute((Void[]) null);
}

は次のDownloadTaskようになります。

@Override
protected List<String> doInBackground(Void... voids) {
     // Download list of URLs from server, etc.
}

@Override
protected void onPostExecute(List<String> urls) {
    mHandler.sendEmptyMessageDelayed(111, 5000);
}

ハンドラーが呼び出され、onPreExecute (AsyncTask 内) が呼び出され、DownloadTask (右のonCreate) の初期実行も機能します。

この質問: Android SDK AsyncTask doInBackground not running (subclass)によると、SDK15 に関連している可能性があります。

ヒントをありがとう。


更新Handler が UI スレッドにない可能性があるというコメントを受け取ったので ( Thread.currentThreadonCreate メソッドと Handlers メソッドの両方で同じであるため、奇妙です)、メソッドを次のようhandleMessageに修正しました。handleMessage

mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(mDownloadTask != null) {
                    mDownloadTask.cancel(true);
                }
                mDownloadTask = new DownloadTask();
                mDownloadTask.execute((Void[]) null);
            }
        });
    }
};

まだ成功していません。


完全な DownloadTask クラスを更新する

class DownloadTask extends AsyncTask<Void, Void, List<String>> {

    @Override
    protected void onPreExecute() {
        // Cancel the animation.
        if (mSlideshowAnimation != null) {
            mSlideshowAnimation.cancel(true);
        }

        mImageView1.setVisibility(View.GONE);
        mImageView2.setVisibility(View.GONE);
        animate(mProgressBar).alpha(1.0f).setDuration(500).start();

        Log.d(TAG, "Download preparation done.");
    }

    @Override
    protected List<String> doInBackground(Void... voids) {
        Log.d(TAG, "Download");
        SharedPreferences s = getSharedPreferences("access", Context.MODE_PRIVATE);
        String token = s.getString("token", null);

        Log.d(TAG, "Downloading slideshows.");

        List<String> urls = new ArrayList<String>();
        Slideshow[] slideshows = new Api(SlideshowActivity.this).getSlideshows(token);
        for (Slideshow slideshow : slideshows) {
            urls.addAll(slideshow.getAllPhotoUrls());
        }

        Log.d(TAG, "Downloading slideshows: " + slideshows.length);

        for (String url : urls) {
            try {
                url = Api.HOST + url;

                if (!Cache.fileExists(Cache.getCacheFilenameForUrl(SlideshowActivity.this, url))) {
                    Cache.cacheStream(SlideshowActivity.this, HttpHelper.download(SlideshowActivity.this, url), url);
                } else {
                    Log.d(TAG, "Cached: " + url);
                }
            } catch (IOException e) {
                Log.e(TAG, "Error while downloading.", e);
            }
        }

        Log.d(TAG, "Downloading slideshows finished.");

        return urls;
    }

    @Override
    protected void onPostExecute(List<String> urls) {
        Log.d(TAG, "download successful");
        animate(mProgressBar).alpha(0.0f).setDuration(500).start();

        mCurrentImageIndex = -1;
        mImageUrls = urls;

        mSlideshowAnimation = new SlideshowAnimation();
        mSlideshowAnimation.execute((Void[]) null);

        mHandler.sendEmptyMessageDelayed(111, 5000);
    }
}
4

2 に答える 2

14

Waqas との有益な議論のおかげで (ありがとう!)、コードのエラーを最終的に発見しました。実際、上記の記述はすべて正しく、そのまま機能します。私の場合の問題は、2 番目のタスクが最初のタスクをブロックし、その逆も同様でした。

Google グループでこの投稿を見つけたのはおそらく偶然でした: http://groups.google.com/group/android-developers/browse_thread/thread/f0cd114c57ceefe3?tvc=2&q=AsyncTask+in+Android+4.0。スレッド化に関係するすべての人が、この説明を注意深く読むことをお勧めします。

AsyncTask は、スレッド モデルをシリアル エグゼキューターに (再び) 切り替えました。これは、2 つの AsyncTasks を持つという私のアプローチと互換性がないようです。

最後に、「ダウンロード」の処理をクラシックに切り替え、必要に応じてスライドショーをキャンセルするメッセージを投稿するためにThread使用しました。HandlerハンドラsendEmptyMessageDelayedを使用して、しばらくしてダウンロード スレッドを再作成し、データを更新します。

すべてのコメントと回答に感謝します。

于 2012-05-02T02:20:15.773 に答える
3

Handlerは Android のスレッドの一種で、AsyncTask も別のスレッドで実行されます。AsyncTask を使用する場合、ルールはほとんどありません。

このクラスが正しく機能するためには、いくつかのスレッド化ルールに従う必要があります。

タスク インスタンスは、UI スレッドで作成する必要があります。execute(Params...) は UI スレッドで呼び出す必要があります。onPreExecute()、onPostExecute(Result)、doInBackground(Params...)、onProgressUpdate(Progress...) を手動で呼び出さないでください。タスクは 1 回だけ実行できます (2 回目の実行を試みると、例外がスローされます)。

したがって、AsyncTAsk は UI スレッドから呼び出す必要があることを明確に示しています。UI スレッドではない Handler から呼び出すと...

これも試してみてください

mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if(mDownloadTask != null) {
                mDownloadTask.cancel(true);
            }
           if([isCancelled()][1]){
            mDownloadTask = new DownloadTask();
            mDownloadTask.execute((Void[]) null);
          } // i assume your task is not getting cancelled before starting it again..
        }
    });
  }
};

また、ドキュメントにはこれが記載されています..

Handler には主に 2 つの用途があります。

(1) メッセージとランナブルを将来のある時点で実行するようにスケジュールする。(2) 自分のスレッドとは異なるスレッドで実行されるアクションをキューに入れる。

于 2012-04-26T07:09:27.283 に答える