3

ListViewローカル データベースから取得したいくつかの武器を表示するカスタムがあります。getView()合計 88 行あり、行ごとにテキストと画像が呼び出されるたびに設定されます。高速スクロール中ListViewに遅延が発生し、ガベージ コレクターが異常になり、1 秒あたり 1M オブジェクトが削除されます。理由がわかりません。

実装を投稿する前にAdapter、イメージの設定方法について説明します。私のWeaponクラスは、セッターとゲッターを持つ単なるデータホルダーです。これは、データベースが作成されるときに名前と画像が設定される方法です (非常に奇妙に思えるかもしれませんが、他のすべてのソリューションはさらにゆっくりと動作します)。

    private Weapon buildWeapon(Cursor cursor) {
    Weapon w = new Weapon();
    w.setId(cursor.getLong(0));
    w.setName(cursor.getString(1));
    w.setImage(Constants.ALL_WEAPON_IMAGES[(int) cursor.getLong(0)-1]);


    return w;
}

そのため、Arrayすべての武器の画像を の形式で含む がありR.drawable.somegunます。データ構造は、ID-1 が常に my の正しいドローアブル参照を指すように実装されていArrayます。Weapon クラスの画像フィールドはInteger. これで、私の方法がどのように機能するかがわかりました。getImage()以下は私の方法Adapterです。

 public class Weapon_Adapter extends BaseAdapter {
private List<Weapon> items;
private LayoutInflater inflater = null;
private WeaponHolder weaponHolder;
private Weapon wp;


static class WeaponHolder {
    public TextView text;
    public ImageView image;
}

// Context and all weapons of specified class are passed here

public Weapon_Adapter(List<Weapon> items, Context c) {
    this.items = (List<Weapon>) items;
    inflater = LayoutInflater.from(c);
    Log.d("Adapter:", "Adapter created");
}

@Override
public int getCount() {
    return items.size();
}

@Override
public Weapon getItem(int position) {
    return items.get(position);
}


@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    wp = (Weapon) getItem(position);

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.category_row, null);
        weaponHolder = new WeaponHolder();
        weaponHolder.text = (TextView) convertView
                .findViewById(R.id.tvCatText);
        weaponHolder.image = (ImageView) convertView
                .findViewById(R.id.imgCatImage);
        convertView.setTag(weaponHolder);
    }

      weaponHolder = (WeaponHolder) convertView.getTag();   


    weaponHolder.text.setText(wp.getName());
    weaponHolder.image.setImageResource(wp.getImage());
           // weaponHolder.image.setImageResource(R.drawable.ak74m);




    return convertView;

}}

奇妙なことに、コメントアウトされた行を使用してすべてのアイテムに同じ画像を静的に設定すると、一度も呼び出されずにすべてのラグと GC が削除されます! 私はそれを得ていません..wp.getImage()まったく同じものを返しますが、R.drawable.name武器ごとに異なるだけです。しかし、GC はListViewスクロール中に大量のオブジェクトとラグを取り除きます。私が間違っていることはありますか?

アップデート

画像の設定をに移動しましたがAsyncTask、ラグはなくなりました:

    public class AsyncImageSetter extends AsyncTask<Void, Void, Void> {

private ImageView img;
private int image_resId;
private Bitmap bmp;
private Context c;

public AsyncImageSetter(Context c, ImageView img, int image_ResId, Bitmap bmp) {

    this.img = img;
    this.image_resId = image_ResId;
    this.bmp = bmp;
    this.c = c;

}

@Override
protected Void doInBackground(Void... params) {

    bmp = BitmapFactory.decodeResource(c.getResources(), image_resId);

    return null;
}

@Override
protected void onPostExecute(Void result) {

    img.setImageBitmap(bmp);
    bmp = null;

    super.onPostExecute(result);
}

   }

ただし、GC は依然として狂ったように呼び出され、リスト全体を上下にスクロールすると RAM の消費量が増加します。問題は、RAM 使用量の増加を回避するためにイメージのリサイクルを最適化するにはどうすればよいかということです。

4

2 に答える 2

3

通常、すべてのビットマップを Android のメモリにロードするのは現実的ではないため、時々 GC を取得すると想定する必要があります。

ただし、次のヒントを検討してください。

  1. ビットマップを表示するために必要なサイズに縮小します。Google の方法またはの方法を使用できます。

  2. 画像ファイルを入れたフォルダを確認してください。多くの人がそれらを res/drawable フォルダーに入れ、元のサイズよりもはるかに大きくなる理由を理解していません (密度のためです。デバイスはおそらく xhdpi または xxhdpi ですが、mdpi です)。

    たとえば、画像が drawable フォルダーにあり、xhdpi デバイス (Galaxy S3 など) で実行する場合、 (WIDTH*2)*(HEIGHT*2)*4 バイトかかります。画像が 200x200 の場合、ビットマップ オブジェクトは少なくとも 400*400*4=640,000 バイトかかります。galaxy s4 や htc などの xxhdpi デバイスではさらに悪化します。

  3. LruCacheのようなメモリキャッシュの使用を検討してください

  4. ビットマップに透明度がなく、品質に違いが見られない場合は、デフォルトの設定ではなくRGB_565 設定を使用することを検討してください。これには、ピクセルあたり 4 バイトではなく、ピクセルあたり 2 バイトが必要です。

  5. 十分な責任を負うことができる場合は、キャッシングに JNI を使用できます。ここに、このタスク用の小さなコードを作成しました。私がそこに書いたメモをすべて読んでください。

ところで、画像に識別子の配列を使用していることに気付きました。画像の名前に何らかのロジックが含まれている場合 (例: img1、img2、...)、代わりに getResources().getIdentifier(...) を使用できます。

于 2013-08-25T18:51:14.997 に答える
1

低 fps の問題を修正し、高速スクロールがスムーズに実行されるようになった場合は、実際に問題なく使用できます。

頻繁な GC アクションが表面的な問題であり、直面している問題OutOfMemoryExceptionやその他の欠点がない場合は、おそらくそのままにしておく必要があります。AsyncTaskそれができない場合は、別の方法があります。ダウン サンプリングとキャッシュに加えて、 の起動後、実際にリソース ファイルを取得する前に、人為的に短い待機時間 (50 ~ 150 ミリ秒) を追加することもできます。次に、人為的な遅延の後にチェックする必要があるキャンセル フラグをタスクに追加します。設定されているtrue場合は、リソース ファイルを要求しません。

いくつかの (実行可能ではない) コード例:

class MyImageLoader extends AsyncTask {
    private boolean cancel = false

    private Bitmap bitmap;

    public void cancel() { cancel = true }

    public void doInBackground() {
        sleep(100);
        if(!cancel) {
            bitmap = BitmapFactory.decodeResource(...);
        }
    }
}

class Adapter {

    static class WeaponHolder {
        public TextView text;
        public ImageView image;
        public MyImageLoader loader;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        WeaponHolder holder;

        if (convertView == null) {
            ...
            holder = new WeaponHolder();
        } else {
            holder = convertView.getTag();
            holder.loader.cancel(); // Cancel currently active loading process
        }

        holder.loader = new MyImageLoader();
        holder.loader.execute();

        return convertView;
    }
}

このようにして、ユーザーが非常に速くスクロールしている場合、ほとんどの画像は内部メモリから読み取られず、十分なメモリを節約できます。

于 2013-08-25T19:59:21.443 に答える