5

さまざまな Android アプリケーションの場合、大きなListViews、つまり 100 ~ 300 エントリのビューが必要です。

アプリケーションの起動時にすべてのエントリを一括でロードする必要があります。ソートと処理が必要であり、アプリケーションは最初に表示する項目を認識できないためです。

これまでのところ、すべてのアイテムの画像も一括で読み込んでおり、ArrayList<CustomType>各エントリの残りのデータと一緒に保存されています。

ただし、もちろん、これは良い方法ではありません。次のような問題が発生する可能性が非常に高いためOutOfMemoryExceptionですArrayList

したがって、最善の解決策は、明らかに、テキスト データのみを一括で読み込み、必要に応じて画像を読み込むことですよね? たとえば、Google Play アプリケーションでは次のように処理されます。スクロールすると画像が読み込まれることがわかります。つまり、おそらくアダプタのgetView()メソッドで読み込まれます。しかし、Google Play の場合、画像はインターネットからロードする必要があるため、これは別の問題ですが、私には当てはまりません。私の問題は、画像の読み込みに時間がかかりすぎることではありませんが、画像を保存するにはメモリが多すぎることです。

では、画像はどうすればよいのでしょうか。getView()本当に必要なときにロードしますか? スクロールが遅くなります。それで、それから電話しAsyncTaskますか?それとも普通のThread?パラメータ化しますか?

既にロードされているイメージを に保存できるHashMap<String,Bitmap>ので、 に再度ロードする必要はありませんgetView()。しかし、これを行うと、メモリの問題が再び発生します。HashMapストアはすべての画像への参照を格納するため、最終的には再び問題が発生する可能性がありますOutOfMemoryException

ここには、画像の「遅延読み込み」について議論する質問がすでにたくさんあることを知っています。しかし、それらは主に読み込みが遅いという問題をカバーしており、メモリ消費量が多すぎません。

編集: getView() で AsyncTasksを開始することにしました。これにより、画像がバックグラウンドで ListView に読み込まれます。しかし、これによりアプリケーションが RejectedExecutionException に遭遇します。私は今どうすればいい?

4

7 に答える 7

9

AsyncTask を使用してイメージをロードし、アダプターの getView 関数でタスクをビューにアタッチして、どのタスクがどのビューにロードされているかを追跡するというアプローチを取りました。私はこれを私のアプリで使用していますが、スクロールの遅れはなく、例外がスローされることなく、すべての画像が適切な位置に読み込まれます。また、キャンセルされた場合、タスクは機能しないため、リストでフリングを実行できますが、まったく遅れるはずです.

タスク:

public class DecodeTask extends AsyncTask<String, Void, Bitmap> {

private static int MaxTextureSize = 2048; /* True for most devices. */

public ImageView v;

public DecodeTask(ImageView iv) {
    v = iv;
}

protected Bitmap doInBackground(String... params) {
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPurgeable = true;
    opt.inPreferQualityOverSpeed = false;
    opt.inSampleSize = 0;

    Bitmap bitmap = null;
    if(isCancelled()) {
        return bitmap;
    }

    opt.inJustDecodeBounds = true;
    do {
        opt.inSampleSize++;
        BitmapFactory.decodeFile(params[0], opt);
    } while(opt.outHeight > MaxTextureSize || opt.outWidth > MaxTextureSize)
    opt.inJustDecodeBounds = false;

    bitmap = BitmapFactory.decodeFile(params[0], opt);
    return bitmap;
}

@Override
protected void onPostExecute(Bitmap result) {
    if(v != null) {
        v.setImageBitmap(result);
    }
}

}

アダプターは、ロードする必要があるすべてのイメージのファイル パスを含む ArrayList を格納します。getView 関数は次のようになります。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView iv = null;
    if(convertView == null) {
        convertView = getLayoutInflater().inflate(R.id.your_view, null); /* Inflate your view here */
        iv = convertView.findViewById(R.id.your_image_view);
    } else {
        iv = convertView.findViewById(R.id.your_image_view);
        DecodeTask task = (DecodeTask)iv.getTag(R.id.your_image_view);
        if(task != null) {
            task.cancel(true);
        }
    }
    iv.setImageBitmap(null);
    DecodeTask task = new DecodeTask(iv);
    task.execute(getItem(position) /* File path to image */);
    iv.setTag(R.id.your_image_view, task);

    return convertView;
}

注意: バージョン 1.5 から 2.3 では、AsyncTask にスレッド プールを使用するため、メモリの問題が発生する可能性があります。3.0+ では、AsyncTasks を実行するためにデフォルトでシリアル モデルに戻ります。これにより、一度に 1 つのタスクが実行されるため、常にメモリの使用量が少なくなります。ただし、画像が大きすぎない限り、問題ありません。

更新:このソリューションは引き続き機能しますが、この問題をよりクリーンな方法で解決するために、オープン ソース コミュニティにすばらしい追加が行われています。Glide や Picasso などのライブラリはどちらも、リスト内の項目の読み込みを非常にうまく処理します。可能であれば、これらのソリューションのいずれかを検討することをお勧めします。

于 2012-09-21T14:19:59.360 に答える
2

1)メモリの問題を解決するには:必要に応じて画像をリサイクルHashMap<String, Bitmap>できるように使用できますか?画像への弱い参照がある場合、最初に述べたものでもWeakHashMap同じことが機能するはずです。ArrayListCustomType

2)ユーザーがリストをスクロールしている間は画像をロードしないのではなく、ユーザーがスクロールを停止したときに画像をロードするのはどうですか。はい、リストは画像がないと見栄えがよくありませんが、スクロール中は非常に効率的であり、ユーザーがスクロールしている間は詳細が表示されません. 技術的には、次のように動作するはずです。

listView.setOnScrollListener(new OnScrollListener() {
  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
      int totalItemCount) {
    // Don't care.
  }

  @Override
  public void onScrollStateChanged(final AbsListView view, int scrollState) {
    // Load images here. ListView can tell you what rows are visible, you load images for these rows and update corresponding View-s.
  }
})

3) アプリの起動時に約 300 個の画像を読み込み、アプリの有効期間中保持するアプリがあります。それは私にとっても問題ですが、これまでのところ OOM の報告はほとんど見たことがなく、別のリークが原因で発生したと思われます。ListView 画像のプロファイルを使用してメモリを節約しようとしRGB_565ます (この目的では品質に大きな違いはありません)。96x96 の最大画像サイズを使用します。これは、標準のリスト項目の高さには十分なはずです。

于 2012-09-17T23:44:20.717 に答える
1

重すぎるため、すべての画像をリストに保存しないでください。getView で AsyncTask を開始し、InBackground で画像を取得してデコードし、PostExecute で imageView に画像を描画する必要があります。リストのパフォーマンスを維持するために、getView メソッドから convertView パラメーターを使用することもできますが、AsyncTask が終了する前にビューをリサイクルできるため、AsyncTask では複雑になり始め、この余分な処理を行う必要があります...

LruCache を使用することもできますが、これは画像がインターネットからダウンロードされた場合にのみ意味があります。それらがローカルに保存されている場合、それを使用しても意味がありません。

于 2012-09-13T22:34:21.047 に答える
1

これをチェックしてください:https ://github.com/DHuckaby/Prime

私はこれをListViewの画像に使用するか、自分で解決しようとします..実際にアプリのすべてのリモート画像に使用します..または少なくともソースコードを読みます..画像管理は苦痛です..成熟したものに頼るあなたを動かすための図書館。

于 2012-09-21T03:56:43.430 に答える
0

あなたが提供したリンクは、convertView、asyncTask などを理解するのに適していますView v = super.getView(position, convertView, parent);。ビューをリサイクルしたい場合は、そうするべきですif(convertView != null){ myView = convertView} else{ inflate(myView)};

AsyncTask については、API ごとに異なりますが、古い API に execute() を使用し、新しい API に executeOnExecutor を使用すると、すべて問題ないと思います。URI と ImageView を AsyncTask に渡す必要があります。ここでは、AsyncTask がそのイメージで作業している新しい行に表示される convertView に問題がある可能性があります。たとえば、ImageView-AsyncTask のハッシュマップを保持して、無効になったこれらをキャンセルできます。

PS.新しいコメントを作成して申し訳ありませんが、インライン回答には長すぎました:)

于 2012-09-14T06:51:46.723 に答える
-1
    private class CallService extends AsyncTask<String, Integer, String>
         {   
                protected String doInBackground(String... u)
                {
                    fetchReasons();
                    return null;
                }
                protected void onPreExecute() 
                {
                    //Define the loader here.

                }
                public void onProgressUpdate(Integer... args)
                {           
                }
                protected void onPostExecute(String result) 
                {               
                    //remove loader
                    //Add data to your view.
                }
         }

public void fetchReasons()
    {
         //Call Your Web Service and save the records in the arrayList
    }

new CallService().execute(); を呼び出します。onCreate() メソッドで

于 2012-09-20T10:16:41.517 に答える
-3

小さなアドバイスは、フリングが発生したときに画像の読み込みを無効にすることです。


すべてを高速化するための汚いハック:

アダプタのgetItemViewType(int position)で、位置を返します。

@Override
public long getItemViewType(int position) {
     return position;
}
@Override
public long getViewTypeCount(int position) {
     return getCount();
}
@Override
public View getView(int position, View convertView, ViewGroup arg2) {
    if (convertView == null) {
        //inflate your convertView and set everything
    }
    //do not do anything just return the convertView
    return convertView;
}

ListViewキャッシュする画像の量を決定します。


于 2012-09-17T00:21:58.380 に答える