0

リスト アクティビティを使用して、インターネットから取得したアイテムのリストを表示する Android アプリを入手しました。最初に AsyncTask を使用してリストをロードし、その非同期タスクが完了すると、別の非同期タスクを呼び出して、リスト アイテムに付随するサムネイル画像のロードを開始します。私が抱えている問題は、ユーザーがいつでも押すことができる更新ボタンにアクセスでき、それを押すと、アイテムのリスト全体が削除され、読み込みが最初からやり直すことです。これが発生した場合、サムネイルをロードする Async タスクがまだ実行されている可能性があり、存在しないリスト項目にサムネイルを追加しようとする可能性があります。ブール値を使用してリストを同期しようとしましたが、調査した結果、機能しないことがわかりました。また、静的アトミック ブール値を使用して、サムネイル ローダーをキャンセルするために更新がヒットしたかどうかを確認しようとしました。

public class LoadItems extends AsyncTask<Void, Void, Boolean> {

private Activity activity;
private static boolean loading = false;
public static final AtomicBoolean refreshing = new AtomicBoolean(false);
private static final String TAG = "LoadItems";
private int start;

private List<ListItem> items;

public LoadItems(Activity activity) {
    this.activity = activity;
}

@Override
protected void onPreExecute() {
    loading = true;
    start = ItemViewer.itemList.size();
}

@Override
protected Boolean doInBackground(Void... arg0) {

    items = WebFunctions.getMoreItems(activity);
    return (items != null);
}


protected void onPostExecute(Boolean success) {
    if (success) {
        for (ListItem item: items) {
            ItemViewer.itemList.add(item);
            Log.d(TAG, "added item " + item.getTitle());
        }
        LoadThumbnails thumbnailLoader = new LoadThumbnails();
        thumbnailLoader.execute(start, ItemViewer.itemList.size());
    }
    loading = false;
}

public void protectedExecute() {
    if (!loading)
        execute();
}

public void refresh() {
    if (!refreshing.getAndSet(true)) {
        WebFunctions.reset();
        ItemViewer.itemList.removeAllItems();
        execute();
    }
}

}



public class LoadThumbnails extends AsyncTask<Integer, Void, Drawable> {
private int position;
private int end;

@Override
protected Drawable doInBackground(Integer... params) {
    position = params[0];
    end = params[1];
    Drawable thumbnail = null;
    synchronized(ItemViewer.itemList) {
        if (LoadItems.refreshing.get())
            cancel(true);
        String url = ItemViewer.itemList.get(position).getThumbnailUrl();
        if (!url.isEmpty())
            thumbnail = WebFunctions.loadDrawableFromUrl(ItemViewer.activity, url);
    }
    return thumbnail;
}

protected void onPostExecute(Drawable d) {

    synchronized (ItemViewer.itemList) {
        if (LoadItems.refreshing.get())
            cancel(true);
        if (d != null)
            ItemViewer.itemList.setThumbnail(position, d);
        position++;
        if (position < end) {
            LoadThumbnails lt = new LoadThumbnails();
            lt.execute(position, end);
        }
    }
}

}
4

2 に答える 2

0

次の手順が役立つ場合があります。

  1. 結果をキャッシュします。以前にネットから取得したものはすべて保存され、アプリケーションが起動されたときにすぐに復元されます。このようにして、アプリケーションの起動時に長い遅延と空の画面を回避し、ユーザーが「リロード」を押さないようにします。

  2. ブール変数を作成し、ネットからデータを取得し始めたときに に設定しreload_in_progress、すべてのサムネイルの準備ができたときに に設定します。'reload' クリック ハンドラーは、 が の場合、クリックを無視する必要があります。truefalsereload_in_progresstrue

  3. ユーザーにいくつかの王様を表示するprogress barので、ユーザーは既にリロード中であることを認識し、reload再度プッシュしません。

  4. ほとんど忘れて、ユーザーに表示されるデータを「ライブ」で更新しないでください。これは、ユーザーが変更中にアイテムをクリックし、予想とはまったく異なることを行うという素晴らしい状況につながります。長い更新ではデータを保持し、すべての準備が整ったときにのみ、古いデータを新しいデータにすばやく交換する必要があります。

于 2012-04-21T17:23:55.163 に答える
0

これは非常に簡単に解決できます。ユーザーが更新ボタンを押すたびに、cancel()新しいタスクを作成する前に、作成した最後の非同期タスクを呼び出すようにしてください。例えば、

private void onRefreshClick(View v) {
  if(mLastLoadItemTask != null) mLastLoadItemTask.cancel(true);
  if(mLastLoadThumbnailTask != null) mLastLoadThumbnailTask.cancel(true);
  mLastLoadItemTask = new LoadItems(...);
  mLastLoadItemTask.execute();
}

次に、onPostExecute各非同期タスクで、まず を呼び出してキャンセルされたかどうかを確認しますisCancelled()。それらがキャンセルされた場合は、onPostExecuteメソッドが機能しないことを確認してください。例えば、

  protected void onPostExecute(...) {   
         if(isCancelled()) return;
         //Adding items to list 
         //Or start load thumbnail task  
     }

onPostExecuteご覧のとおり、メソッドとcancel呼び出しはすべてメイン therad で行われるため、意図しない更新や古い更新を防ぐことができます。最後に私が提案するのは、必要に応じていつでも isCancelled() をチェックすることで、できるだけ早く作業を停止できるように loadThumbs タスクを変更することです。

于 2012-04-21T17:32:08.783 に答える